当前位置:首页 > 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. { 
  4.   public $cnt = 0; 
  5.  
  6.   public function run() 
  7.   { 
  8.     $this->add(); 
  9.   } 
  10.  
  11.   public function add() 
  12.   { 
  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. //这里输出就是不确定性的 
  29. var_dump($c->cnt); 

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

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

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

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

结果如下所示:

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

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

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

结果如下图所示:

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

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

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

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

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

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

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

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

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

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

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

Tags: pthreads synchronized

分享到: