Big Bug Ban

兴趣 践行 创新

Archive for the ‘事务’ tag

线程安全和一条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

其中左边是线程1 右边是线程2

从上至下假设为我们的线程执行顺序  线程1先执行了

请注意绿色划线部分

线程2得到的$money的值是100!

因为这个时候线程1的update尚未执行

于是当2执行到后面的时候也进行了一次减法

于是就会出现-100 -200的情况

甚至不加for循环,快速刷新一下也会出现这个问题

 

这说明php的执行并非线程安全,那个所谓的线程安全版本根本就不是这个意思

想来也可以理解,如果真的线程安全了,执行效率很降很多很多很多,因为第二个线程必须等待第一个释放了才能执行

这篇文章先写到这里,下一篇会告诉大家如何解决以及新发生的问题

Written by princehaku

9月 16th, 2011 at 12:57 上午