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

时间:2021-12-30 16:40:20

本文实例讲述了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用法示例

希望本文所述对大家PHP程序设计有所帮助。

原文链接:https://www.cnblogs.com/jkko123/p/8877137.html