当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP pthreads v3下同步处理synchronized用法示例

发布:smiling 来源: PHP粉丝网  添加日期:2022-02-13 13:01:34 浏览: 评论:0 

本文实例讲述了PHP pthreads v3下同步处理synchronized用法,分享给大家供大家参考,具体如下:

之所以会用到同步,是因为如果多个线程中对同一个资源进行操作时,会发生混乱。

比如2个线程对变量进行加1操作,第1个线程还没来的及改写数据时,第2个线程就对变量进行操作了,那变量最终的结果就是未知的,这个时候就需要同步来进行控制了。

例子如下:

  1. <?php 
  2. class Count extends Thread 
  3.   public $cnt = 0; 
  4.  
  5.   public function run() 
  6.   { 
  7.     $this->add(); 
  8.   } 
  9.  
  10.   public function add() 
  11.   { 
  12.     //对成员进行加1操作 
  13.     for ($i = 0; $i < 100000; $i++) { 
  14.       ++$this->cnt; 
  15.     } 
  16.   } 
  17.  
  18. $c = new Count(); 
  19. //调用start()时,线程run()中就调用了add()方法 
  20. $c->start(); 
  21. //我们人为再调用一次add()方法,这时候就会有两个for循环对$cnt进行操作 
  22. $c->add(); 
  23. //把创建的线程加入主线程中,让主线程等待子线程运行结束 
  24. $c->join(); 
  25.  
  26. //这里输出就是不确定性的 
  27. var_dump($c->cnt); 

多次运行后,$cnt的值是不确定的,如下图所示:

PHP pthreads v3下同步处理synchronized用法示例

在pthreads v2中我们可以用Mutex,不过在v3版本中被删除了,所以我们可以简单的把加1操作放到synchronized中进行同步,代码如下:

  1. <?php 
  2. class Count extends Thread 
  3.   public $cnt = 0; 
  4.  
  5.   public function run() 
  6.   { 
  7.     $this->add(); 
  8.   } 
  9.  
  10.   public function add() 
  11.   { 
  12.     $this->synchronized(function () { 
  13.       //对成员进行加1操作 
  14.       for ($i = 0; $i < 100000; $i++) { 
  15.         ++$this->cnt; 
  16.       } 
  17.     }); 
  18.   } 
  19.  
  20. $c = new Count(); 
  21. //调用start()时,线程run()中就调用了add()方法 
  22. $c->start(); 
  23. //我们人为再调用一次add()方法,这时候就会有两个for循环对$cnt进行操作 
  24. $c->add(); 
  25. //把创建的线程加入主线程中,让主线程等待子线程运行结束 
  26. $c->join(); 
  27.  
  28. //这里就会一直输出200000 
  29. var_dump($c->cnt); 

结果如下所示:

PHP pthreads v3下同步处理synchronized用法示例

当然我们也可以通过notify()和wait()进行同步控制,代码如下:

  1. <?php 
  2. class Task extends Thread 
  3.   public $flag = 1
  4.  
  5.   public function run() 
  6.   { 
  7.     $this->synchronized(function () { 
  8.       //标识不为1就一直等待 
  9.       if ($this->flag !== 1) { 
  10.         $this->wait(); 
  11.       } 
  12.  
  13.       for ($i = 1; $i <= 10; $i++) { 
  14.  
  15.         echo "flag : {$this->flag} i : {$i} \n"
  16.  
  17.         if ($this->flag === 1) { 
  18.           //设置标识 
  19.           $this->flag = 2
  20.           //发送唤醒通知,然后让当前线程等待 
  21.           //注意,notify()与wait()顺序不要搞错了,不然会一直阻塞 
  22.           $this->notify(); 
  23.           $this->wait(); 
  24.         } 
  25.       } 
  26.  
  27.       //我们在这里再次调用notify() 
  28.       //因为在最后一次输出flag : 2 i : 20时,当前线程的i已经变成11了,跳出了for循环, 
  29.       //但另一个线程则一直阻塞在wait()那里,程序无法结束,所以需要notify()再次唤醒一次 
  30.       $this->notify(); 
  31.     }); 
  32.   } 
  33.  
  34. $t = new Task(); 
  35. $t->start(); 
  36.  
  37. $t->synchronized(function ($obj) { 
  38.   //标识不为2就一直等待 
  39.   if ($obj->flag !== 2) { 
  40.     $obj->wait(); 
  41.   } 
  42.  
  43.   for ($i = 11; $i <= 20; $i++) { 
  44.  
  45.     echo "flag : {$obj->flag} i : {$i} \n"
  46.  
  47.     if ($obj->flag === 2) { 
  48.       $obj->flag = 1
  49.       $obj->notify(); 
  50.       $obj->wait(); 
  51.     } 
  52.   } 
  53. }, $t); 
  54.  
  55. //把创建的线程加入主线程中,让主线程等待子线程运行结束 
  56. $t->join(); 

结果如下图所示:

PHP pthreads v3下同步处理synchronized用法示例

我们通过notify()和wait()控制了两个for循环,来回的输出变量i的值,保证了顺序性。

我们再来看一个复杂点的例子,共享的资源,如果不进行同步操作,会出现不可预知的情况,代码如下: 

  1. <?php 
  2. class Task extends Thread 
  3.   private $name
  4.   private $file
  5.  
  6.   public function __construct($name$file
  7.   { 
  8.     $this->name = $name
  9.     $this->file = $file
  10.   } 
  11.  
  12.   public function run() 
  13.   { 
  14.     $data = file_get_contents($this->file); 
  15.     $data = floatval($data); 
  16.     for ($i = 0; $i < 100000; $i++) { 
  17.       ++$data
  18.     } 
  19.     file_put_contents($this->file, $data); 
  20.     echo "task : {$this->name} data : {$data} \n"
  21.   } 
  22.  
  23. $tasks = []; 
  24. $file = './test.log'
  25.  
  26. for ($i = 0; $i < 100; $i++) { 
  27.   $tasks[$i] = new Task($i$file); 
  28.   $tasks[$i]->start(); 
  29.  
  30. for ($i = 0; $i < 100; $i++) { 
  31.   $tasks[$i]->join(); 

我们开100个线程对文件test.log进行读写,理想状态下,test.log中的数据应该是每次增加10000000的。现在的电脑配置都比较好,大家可以多运行几次就可以看出效果。

PHP pthreads v3下同步处理synchronized用法示例

很明显最后的数据好像少了200000,多线程下对test.log文件进行读写,而我们又没有加锁,显然是会出现数据混乱的。

现在我们修改一下代码,如下:

  1. <?php 
  2. class File extends Thread 
  3.   private $file
  4.  
  5.   public function __construct($file
  6.   { 
  7.     $this->file = $file
  8.   } 
  9.  
  10.   public function inc() 
  11.   { 
  12.     //进行同步控制,当100个task线程调用inc方法时,synchronized可以保证块内的代码是同步的 
  13.     //注意,注意,不要把inc方法写到Task里,那样是没效果的,因为每个task线程都是独立空间,他们各自调各自的inc方法,是没法做到同步的 
  14.     //常用的做法是我们要同步哪些资源,就为这些资源写个Thread类,并提供操作这些资源的方法,并在方法里加上synchronized 
  15.     return $this->synchronized(function () { 
  16.       $data = file_get_contents($this->file); 
  17.       $data = floatval($data); 
  18.       for ($i = 0; $i < 100000; $i++) { 
  19.         ++$data
  20.       } 
  21.       file_put_contents($this->file, $data); 
  22.       return $data
  23.     }); 
  24.   } 
  25.  
  26. class Task extends Thread 
  27.   private $name
  28.   private $file
  29.  
  30.   public function __construct($name$file
  31.   { 
  32.     $this->name = $name
  33.     $this->file = $file
  34.   } 
  35.  
  36.   public function run() 
  37.   { 
  38.     $data = $this->file->inc(); 
  39.     echo "task : {$this->name} data : {$data} \n"
  40.   } 
  41.  
  42. $tasks = []; 
  43. $file = new File('./test.log'); 
  44.  
  45. for ($i = 0; $i < 100; $i++) { 
  46.   $tasks[$i] = new Task($i$file); 
  47.   $tasks[$i]->start(); 
  48.  
  49. for ($i = 0; $i < 100; $i++) { 
  50.   $tasks[$i]->join(); 

结果如下图所示,当然为了保险起见,我们可以试着多运行几次,下面是我运行了25次的结果:

PHP pthreads v3下同步处理synchronized用法示例

Tags: pthreads synchronized

分享到: