线程安全和一条sql语句引起的思考(一)
这篇文章发布在php分类
因为主要是讨论php的线程安全问题,但是如下遇到的问题在其他的编程环境中也会遇到
首先您肯定会问php官网不是发布过么? 分为线程安全版本和非安全版本
但是既然是线程安全,那言下之意每个进程间相互应该不受干扰.
难道是指所有的进程都需要排队?一个一个的执行
于是抱着问题进行了如下的尝试
<? $host="127.0.0.1"; $user="root"; $passwd="123"; mysql_connect($host,$user,$passwd); mysql_select_db("test"); mysql_query("set names utf8"); //查询是否是大于0的 $res=mysql_query("select * from test where id=1 and k=100"); $res=mysql_fetch_array($res); $money=$res['k']; //如果大于0 就减100 if($money>0){ $res=mysql_query("UPDATE `test` SET `k` = `k`-100 where id=1"); echo "减了!"; }else{ echo "没做操作"; } ?>
代码如上面所示
就是判断k,如果等于100,那么则减去100
数据库test里面只有一个表test
里面的字段为id int k int
只有一条记录
id=1 k=100
好 让我们执行上面的语句
结果一般说来k就会变成0了
但是实际上,问题来了
如果中间的操作耗费了大量的时间,导致延迟更新了
结果又怎样了?
<? $host="127.0.0.1"; $user="root"; $passwd="123"; mysql_connect($host,$user,$passwd); mysql_select_db("test"); mysql_query("set names utf8"); //查询是否是大于0的 $res=mysql_query("select * from test where id=1 and k=100"); $res=mysql_fetch_array($res); $money=$res['k'];
//模拟延迟 这里做一个for循环 模拟中间处理了很多操作
for($i=0;$i<9999999;$i++){
}
//如果大于0 就减100
if($money>0){
$res=mysql_query(“UPDATE `test` SET `k` = `k`-100 where id=1“);
echo “减了!“;
}else{
echo “没做操作“;
}
?>
然后再打开浏览器 重复刷新几次
这个时候结果就不一定是0了! 有可能是-100 –200
视刷新情况而定
而且不管你用所谓的线程安全版本也是同样的结果
为什么么?如果你知道java单例模型中其中的synchronize你应该就明白了
看看这个图
其中左边是线程1 右边是线程2
从上至下假设为我们的线程执行顺序 线程1先执行了
请注意绿色划线部分
线程2得到的$money的值是100!
因为这个时候线程1的update尚未执行
于是当2执行到后面的时候也进行了一次减法
于是就会出现-100 -200的情况
甚至不加for循环,快速刷新一下也会出现这个问题
这说明php的执行并非线程安全,那个所谓的线程安全版本根本就不是这个意思
想来也可以理解,如果真的线程安全了,执行效率很降很多很多很多,因为第二个线程必须等待第一个释放了才能执行
这篇文章先写到这里,下一篇会告诉大家如何解决以及新发生的问题
线程安全不是这个意思- –
你那叫进程的关联性.
…………………………
cdut-boy
16 9月 11 at 2:13 下午
不挖
php在windows下是以多线程方式执行的
官方有两个版本 线程安全和非安全
线程安全就是一个线程执行的时候所占用的资源不能被其他资源访问哇
那线程1占用的sql资源还没有关闭 按理说线程2应该等待 如果是官方说的那个线程安全
但是没懂. 那官方那个版本是啥意思?
servlet执行的时候就是线程非安全的
所有的应该都是.. 不然效率很低很低额..
princehaku
16 9月 11 at 2:57 下午
PHP的线程安全,我觉得值的是解析器,PHP扩展,对于多线程的访问是安全的.
解析器进程Load扩展是多线程安全的.
你用多个网页去访问,这不叫测试线程安全.
cdut-boy
16 9月 11 at 4:14 下午
BTW…在软件园这边打开你的网站,好慢- -!
cdut-boy
16 9月 11 at 4:16 下午