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 相关文章推荐
从网上搜到的phpwind 0day的代码
Dec 07 PHP
PHP 替换模板变量实现步骤
Aug 24 PHP
用php简单实现加减乘除计算器
Jan 06 PHP
yii中widget的用法
Dec 03 PHP
PHP获取文件夹大小函数用法实例
Jul 01 PHP
php实现搜索一维数组元素并删除二维数组对应元素的方法
Jul 06 PHP
php官方微信接口大全(微信支付、微信红包、微信摇一摇、微信小店)
Dec 21 PHP
PHP远程连接oracle数据库操作实现方法图文详解
Apr 11 PHP
thinkphp5.1框架模板布局与模板继承用法分析
Jul 19 PHP
Thinkphp5.0框架视图view的模板布局用法分析
Oct 12 PHP
php use和include区别总结
Oct 13 PHP
php的lavarel框架中join和orWhere的用法
Dec 28 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新手上路(十一)
2006/10/09 PHP
SWFUpload与CI不能正确上传识别文件MIME类型解决方法分享
2011/04/18 PHP
php实现memcache缓存示例讲解
2013/12/04 PHP
php将日期格式转换成xx天前的格式
2015/04/16 PHP
PHP获取网站中各文章的第一张图片的代码示例
2016/05/20 PHP
PHP chunk_split()函数讲解
2019/02/12 PHP
Javascript 面向对象特性
2009/12/28 Javascript
用JavaScript实现一个代码简洁、逻辑不复杂的多级树
2014/05/23 Javascript
Javascript核心读书有感之语句
2015/02/11 Javascript
深入理解JavaScript中的对象复制(Object Clone)
2016/05/18 Javascript
了解ESlint和其相关操作小结
2018/05/21 Javascript
vue 国际化 vue-i18n 双语言 语言包
2018/06/07 Javascript
angular6.x中ngTemplateOutlet指令的使用示例
2018/08/09 Javascript
Electron-vue开发的客户端支付收款工具的实现
2019/05/24 Javascript
ES6函数实现排它两种写法解析
2020/05/13 Javascript
[42:20]2014 DOTA2华西杯精英邀请赛5 24 DK VS NewBee
2014/05/25 DOTA
Python基础语言学习笔记总结(精华)
2017/11/14 Python
Pandas中把dataframe转成array的方法
2018/04/13 Python
Scrapy基于selenium结合爬取淘宝的实例讲解
2018/06/13 Python
python进行二次方程式计算的实例讲解
2020/12/06 Python
html标签之Object和EMBED标签详解
2013/07/04 HTML / CSS
美国亚洲时尚和美容产品的一站式网上商店:Stylevana
2019/09/05 全球购物
什么是WEB控件?使用WEB控件有哪些优势?
2012/01/21 面试题
实习单位接收函模板
2014/01/10 职场文书
初中三好学生事迹材料
2014/01/13 职场文书
电子工程专业毕业生求职信
2014/03/14 职场文书
幼儿园中班开学寄语
2014/04/03 职场文书
协议书模板
2014/04/23 职场文书
2014年帮扶工作总结
2014/11/26 职场文书
小学生心理健康活动总结
2015/05/08 职场文书
2015年化妆品销售工作总结
2015/05/11 职场文书
医院员工辞职信范文
2015/05/12 职场文书
刑事附带民事代理词
2015/05/25 职场文书
Python实现Telnet自动连接检测密码的示例
2021/04/16 Python
深入详解JS函数的柯里化
2021/06/09 Javascript
Java8 Stream API 提供了一种高效且易于使用的处理数据的方式
2022/04/13 Java/Android