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


Posted in PHP onFebruary 21, 2020

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

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

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

例子如下:

<?php
class Count extends Thread
{
  public $cnt = 0;

  public function run()
  {
    $this->add();
  }

  public function add()
  {
    //对成员进行加1操作
    for ($i = 0; $i < 100000; $i++) {
      ++$this->cnt;
    }
  }
}

$c = new Count();
//调用start()时,线程run()中就调用了add()方法
$c->start();
//我们人为再调用一次add()方法,这时候就会有两个for循环对$cnt进行操作
$c->add();
//把创建的线程加入主线程中,让主线程等待子线程运行结束
$c->join();

//这里输出就是不确定性的
var_dump($c->cnt);

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

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

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

<?php
class Count extends Thread
{
  public $cnt = 0;

  public function run()
  {
    $this->add();
  }

  public function add()
  {
    $this->synchronized(function () {
      //对成员进行加1操作
      for ($i = 0; $i < 100000; $i++) {
        ++$this->cnt;
      }
    });
  }
}

$c = new Count();
//调用start()时,线程run()中就调用了add()方法
$c->start();
//我们人为再调用一次add()方法,这时候就会有两个for循环对$cnt进行操作
$c->add();
//把创建的线程加入主线程中,让主线程等待子线程运行结束
$c->join();

//这里就会一直输出200000
var_dump($c->cnt);

结果如下所示:

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

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

<?php
class Task extends Thread
{
  public $flag = 1;

  public function run()
  {
    $this->synchronized(function () {
      //标识不为1就一直等待
      if ($this->flag !== 1) {
        $this->wait();
      }

      for ($i = 1; $i <= 10; $i++) {

        echo "flag : {$this->flag} i : {$i} \n";

        if ($this->flag === 1) {
          //设置标识
          $this->flag = 2;
          //发送唤醒通知,然后让当前线程等待
          //注意,notify()与wait()顺序不要搞错了,不然会一直阻塞
          $this->notify();
          $this->wait();
        }
      }

      //我们在这里再次调用notify()
      //因为在最后一次输出flag : 2 i : 20时,当前线程的i已经变成11了,跳出了for循环,
      //但另一个线程则一直阻塞在wait()那里,程序无法结束,所以需要notify()再次唤醒一次
      $this->notify();
    });
  }
}

$t = new Task();
$t->start();

$t->synchronized(function ($obj) {
  //标识不为2就一直等待
  if ($obj->flag !== 2) {
    $obj->wait();
  }

  for ($i = 11; $i <= 20; $i++) {

    echo "flag : {$obj->flag} i : {$i} \n";

    if ($obj->flag === 2) {
      $obj->flag = 1;
      $obj->notify();
      $obj->wait();
    }
  }
}, $t);

//把创建的线程加入主线程中,让主线程等待子线程运行结束
$t->join();

结果如下图所示:

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

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

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

<?php
class Task extends Thread
{
  private $name;
  private $file;

  public function __construct($name, $file)
  {
    $this->name = $name;
    $this->file = $file;
  }

  public function run()
  {
    $data = file_get_contents($this->file);
    $data = floatval($data);
    for ($i = 0; $i < 100000; $i++) {
      ++$data;
    }
    file_put_contents($this->file, $data);
    echo "task : {$this->name} data : {$data} \n";
  }
}

$tasks = [];
$file = './test.log';

for ($i = 0; $i < 100; $i++) {
  $tasks[$i] = new Task($i, $file);
  $tasks[$i]->start();
}

for ($i = 0; $i < 100; $i++) {
  $tasks[$i]->join();
}

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

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

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

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

<?php
class File extends Thread
{
  private $file;

  public function __construct($file)
  {
    $this->file = $file;
  }

  public function inc()
  {
    //进行同步控制,当100个task线程调用inc方法时,synchronized可以保证块内的代码是同步的
    //注意,注意,不要把inc方法写到Task里,那样是没效果的,因为每个task线程都是独立空间,他们各自调各自的inc方法,是没法做到同步的
    //常用的做法是我们要同步哪些资源,就为这些资源写个Thread类,并提供操作这些资源的方法,并在方法里加上synchronized
    return $this->synchronized(function () {
      $data = file_get_contents($this->file);
      $data = floatval($data);
      for ($i = 0; $i < 100000; $i++) {
        ++$data;
      }
      file_put_contents($this->file, $data);
      return $data;
    });
  }
}

class Task extends Thread
{
  private $name;
  private $file;

  public function __construct($name, $file)
  {
    $this->name = $name;
    $this->file = $file;
  }

  public function run()
  {
    $data = $this->file->inc();
    echo "task : {$this->name} data : {$data} \n";
  }
}

$tasks = [];
$file = new File('./test.log');

for ($i = 0; $i < 100; $i++) {
  $tasks[$i] = new Task($i, $file);
  $tasks[$i]->start();
}

for ($i = 0; $i < 100; $i++) {
  $tasks[$i]->join();
}

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

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

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

PHP 相关文章推荐
smarty模板中使用get、post、request、cookies、session变量的方法
Apr 24 PHP
php中FTP函数ftp_connect、ftp_login与ftp_chmod用法
Nov 18 PHP
PHP中UNIX时间戳和日期间的转换与计算实例
Nov 19 PHP
微信公众平台消息接口校验与消息接口响应实例
Dec 23 PHP
php+ajax 实现输入读取数据库显示匹配信息
Oct 08 PHP
php mailer类调用远程SMTP服务器发送邮件实现方法
Mar 04 PHP
PHP页面输出搜索后跳转下一页的处理方法
Sep 30 PHP
Centos 6.5系统下编译安装PHP 7.0.13的方法
Dec 19 PHP
PHP进程通信基础之信号量与共享内存通信
Feb 19 PHP
yii2 resetful 授权验证详解
May 18 PHP
laravel 修改.htaccess文件 重定向public的解决方法
Oct 12 PHP
PHP+ajax实现上传、删除、修改单张图片及后台处理逻辑操作详解
Feb 12 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
Feb 21 #PHP
PHP pthreads v3使用中的一些坑和注意点分析
Feb 21 #PHP
php使用pthreads v3多线程实现抓取新浪新闻信息操作示例
Feb 21 #PHP
php操作redis数据库常见方法实例总结
Feb 20 #PHP
php使用redis的几种常见操作方式和用法示例
Feb 20 #PHP
PHP使用openssl扩展实现加解密方法示例
Feb 20 #PHP
php使用redis的有序集合zset实现延迟队列应用示例
Feb 20 #PHP
You might like
php的数组与字符串的转换函数整理汇总
2013/07/18 PHP
php中过滤非法字符的具体实现
2013/10/29 PHP
php中instanceof 与 is_a()区别分析
2015/03/03 PHP
PHP文件操作方法汇总
2015/07/01 PHP
jQuery 图像裁剪插件Jcrop的简单使用
2009/05/22 Javascript
JSQL 批量图片切换的实现代码
2010/05/05 Javascript
input按钮的事件处理大全
2010/12/10 Javascript
javascrip关于继承的小例子
2013/05/10 Javascript
jquery选择器之内容过滤选择器详解
2014/01/27 Javascript
jQuery中:enabled选择器用法实例
2015/01/04 Javascript
jQuery中insertAfter()方法用法实例
2015/01/08 Javascript
js控制页面的全屏展示和退出全屏显示的方法
2015/03/10 Javascript
js+html5实现canvas绘制镂空字体文本的方法
2015/06/05 Javascript
jQuery内容过滤选择器用法示例
2016/09/09 Javascript
全面解析node 表单的图片上传
2016/11/21 Javascript
干货!教大家如何选择Vue和React
2017/03/13 Javascript
利用jquery正则表达式在页面验证url网址输入是否正确
2017/04/04 jQuery
Three.js利用dat.GUI如何简化试验流程详解
2017/09/26 Javascript
Vue.js轮播图走马灯代码实例(全)
2019/05/08 Javascript
微信小程序页面传多个参数跳转页面的实现方法
2019/05/17 Javascript
JavaScript Canvas编写炫彩的网页时钟
2019/10/16 Javascript
Nodejs封装类似express框架的路由实例详解
2020/01/05 NodeJs
vue实现拖拽进度条
2021/03/01 Vue.js
[01:03:51]2018DOTA2亚洲邀请赛 4.7 淘汰赛 VP vs LGD 第三场
2018/04/09 DOTA
基python实现多线程网页爬虫
2015/09/06 Python
对python中的控制条件、循环和跳出详解
2019/06/24 Python
Python中请不要再用re.compile了
2019/06/30 Python
基于Python实现ComicReaper漫画自动爬取脚本过程解析
2019/11/11 Python
Python带参数的装饰器运行原理解析
2020/06/09 Python
python 实现图片修复(可用于去水印)
2020/11/19 Python
html5 canvas实现跟随鼠标旋转的箭头
2016/03/11 HTML / CSS
暑期培训心得体会
2014/09/02 职场文书
技术入股合作协议书
2014/10/07 职场文书
如何写通讯稿
2015/07/22 职场文书
Python 线程池模块之多线程操作代码
2021/05/20 Python
PHP RabbitMQ消息列队
2022/05/11 PHP