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 相关文章推荐
PHP下对数组进行排序的函数
Aug 08 PHP
基于php常用正则表达式的整理汇总
Jun 08 PHP
PHP异常Parse error: syntax error, unexpected T_VAR错误解决方法
May 06 PHP
php打印输出棋盘的实现方法
Dec 23 PHP
php中文繁体和简体相互转换的方法
Mar 21 PHP
php英文单词统计器
Jun 23 PHP
php利用header函数下载各种文件
Aug 24 PHP
PHP读取Excel类文件
May 15 PHP
thinkPHP+phpexcel实现excel报表输出功能示例
Jun 06 PHP
php把字符串指定字符分割成数组的方法
Mar 12 PHP
PHPExcel实现表格导出功能示例【带有多个工作sheet】
Jun 13 PHP
基于PHP实现堆排序原理及实例详解
Jun 19 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用户注册页面利用js进行表单验证具体实例
2013/10/17 PHP
php实现以只读方式打开文件的方法
2015/03/16 PHP
php简单获取文件扩展名的方法
2015/03/24 PHP
PHP保存session到memcache服务器的方法
2016/01/19 PHP
解决 FireFox 下[使用event很麻烦] 的问题.
2006/08/22 Javascript
javascript笔试题目附答案@20081025_jb51.net
2008/10/26 Javascript
JavaScript isArray()函数判断对象类型的种种方法
2010/10/11 Javascript
jquery.combobox中文api和例子,修复了上面的小bug
2011/03/28 Javascript
读jQuery之十四 (触发事件核心方法)
2011/08/23 Javascript
javascript 进阶篇1 正则表达式,cookie管理,userData
2012/03/14 Javascript
javascript学习笔记(十八) 获得页面中的元素代码
2012/06/20 Javascript
利用js实现选项卡的特别效果的实例
2013/03/03 Javascript
详解javascript数组去重问题
2015/11/06 Javascript
深入解析JavaScript编程中的this关键字使用
2015/11/09 Javascript
JS定时器使用,定时定点,固定时刻,循环执行详解
2016/05/31 Javascript
Bootstrap modal 多弹窗之叠加关闭阴影遮罩问题的解决方法
2017/02/27 Javascript
JavaScript实现表单注册、表单验证、运算符功能
2018/10/15 Javascript
js实现无限瀑布流实例方法
2019/09/16 Javascript
ant design vue导航菜单与路由配置操作
2020/10/28 Javascript
Vue实现多页签组件
2021/01/14 Vue.js
Python压缩和解压缩zip文件
2015/02/14 Python
python使用cStringIO实现临时内存文件访问的方法
2015/03/26 Python
用Python实现协同过滤的教程
2015/04/08 Python
Python学生成绩管理系统简洁版
2020/04/05 Python
python pandas读取csv后,获取列标签的方法
2018/11/12 Python
Pytorch技巧:DataLoader的collate_fn参数使用详解
2020/01/08 Python
Python调用ffmpeg开源视频处理库,批量处理视频
2020/11/16 Python
Django中的DateTimeField和DateField实现
2021/02/24 Python
css3实现超立体3D图片侧翻倾斜效果
2014/04/16 HTML / CSS
5个你不知道的HTML5的接口介绍
2013/08/07 HTML / CSS
Html5 FileReader实现即时上传图片功能实例代码
2014/09/01 HTML / CSS
Aurora London官网:奢华、负担得起的皮革手袋
2020/08/01 全球购物
财务会计毕业生自荐信
2013/11/02 职场文书
井冈山红色之旅心得体会
2014/10/07 职场文书
党的群众路线教育实践活动个人整改措施范文
2014/11/04 职场文书
夫妻吵架保证书
2015/05/08 职场文书