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编程最快明白》第二讲 数字、浮点、布尔型、字符串和数组
Nov 01 PHP
php中存储用户ID和密码到mysql数据库的方法
Feb 06 PHP
解析在apache里面给php写虚拟目录的详细方法
Jun 24 PHP
基于preg_match_all采集后数据处理的一点心得笔记(编码转换和正则匹配)
Jan 31 PHP
php判断数组元素中是否存在某个字符串的方法
Jun 14 PHP
PHP实现事件机制实例分析
Jun 26 PHP
Symfony控制层深入详解
Mar 17 PHP
PHP简单获取随机数的常用方法小结
Jun 07 PHP
使用php自动备份数据库表的实现方法
Jul 28 PHP
PHP检查文件是否存在,不存在自动创建及读取文件内容操作示例
Jan 23 PHP
再谈Yii Framework框架中的事件event原理与应用
Apr 07 PHP
php中pcntl_fork详解
Apr 01 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中使用gd库实现下载网页中所有图片
2015/05/12 PHP
PHP面向对象五大原则之里氏替换原则(LSP)详解
2018/04/08 PHP
parseInt parseFloat js字符串转换数字
2010/08/01 Javascript
解决Extjs 4 Panel作为Window组件的子组件时出现双重边框问题
2013/01/11 Javascript
js动态为代码着色显示行号
2013/05/29 Javascript
js 页面元素的几个用法总结
2013/11/18 Javascript
用javascript删除当前行,添加行(示例代码)
2013/11/25 Javascript
Js制作点击输入框时默认文字消失的效果
2015/09/05 Javascript
使用jQuery获取data-的自定义属性
2015/11/10 Javascript
jQuery实现分隔条左右拖动功能
2015/11/21 Javascript
Angular在一个页面中使用两个ng-app的方法
2017/02/20 Javascript
使用mint-ui实现省市区三级联动效果的示例代码
2018/02/09 Javascript
vue2.0 子组件改变props值,并向父组件传值的方法
2018/03/01 Javascript
vue动画之点击按钮往上渐渐显示出来的实例
2018/09/29 Javascript
js面向对象之实现淘宝放大镜
2020/01/15 Javascript
Vue组件通信$attrs、$listeners实现原理解析
2020/09/03 Javascript
Python实现的朴素贝叶斯算法经典示例【测试可用】
2018/06/13 Python
python使用原始套接字发送二层包(链路层帧)的方法
2019/07/22 Python
python MultipartEncoder传输zip文件实例
2020/04/07 Python
如何快速一次性卸载所有python包(第三方库)呢
2020/10/20 Python
Python获取android设备cpu和内存占用情况
2020/11/15 Python
Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)
2021/02/22 Python
深入CSS3 动画效果的总结详解
2013/05/09 HTML / CSS
css3发光搜索表单分享
2014/04/11 HTML / CSS
施华洛世奇英国官网:SWAROVSKI英国
2017/03/13 全球购物
N:Philanthropy官网:美国洛杉矶基础款服装
2020/06/09 全球购物
中西医专业毕业生职业规划书
2014/02/24 职场文书
《伯牙绝弦》教学反思
2014/03/02 职场文书
安全生产大检查方案
2014/05/07 职场文书
公司总经理助理岗位职责
2014/07/09 职场文书
我们的节日元宵活动方案
2014/08/23 职场文书
学校运动会广播稿范文
2014/10/02 职场文书
党员批评与自我批评发言稿
2014/10/14 职场文书
优秀员工事迹材料
2014/12/20 职场文书
初三化学教学反思
2016/02/22 职场文书
Spring整合Mybatis的全过程
2021/06/28 Java/Android