PHP中多线程的两个实现方法


Posted in PHP onOctober 14, 2016

PHP本身是不是支持多线程的,不过我们可以借助其他的方法来实现多线程,比如 shell 服务,比如 web 服务器,本文我们来讲讲这两个方法如何实现。需要的朋友可以来参考一下。

多线程是java中一个很不错的东西,很多朋友说在php中不可以使用PHP多线程了,其实那是错误的说法PHP多线程实现方法和fsockopen函数有关,下面我们来介绍具体实现程序代码,有需要了解的同学可参考。

当有人想要实现并发功能时,他们通常会想到用fork或者spawn threads,但是当他们发现php不支持多线程的时候,大概会转换思路去用一些不够好的语言,比如perl。

其实的是大多数情况下,你大可不必使用 fork 或者线程,并且你会得到比用 fork 或 thread 更好的性能。

假设你要建立一个服务来检查正在运行的n台服务器,以确定他们还在正常运转。你可能会写下面这样的代码:

代码如下:

<?php
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com");
$timeout = 15;
$status = array();
foreach ($hosts as $host) {
$errno = 0;
 $errstr = "";
 $s = fsockopen($host, 80, $errno, $errstr, $timeout);
 if ($s) {
 $status[$host] = "Connectedn";
 fwrite($s, "HEAD / HTTP/1.0rnHost: $hostrnrn");
 do {
 $data = fread($s, 8192);
 if (strlen($data) == 0) {
 break;
 }
 $status[$host] .= $data;
 } while (true);
 fclose($s);
 } else {
 $status[$host] = "Connection failed: $errno $errstrn";
 }
}
print_r($status);
?>

它运行的很好,但是在fsockopen()分析完hostname并且建立一个成功的连接(或者延时$timeout秒)之前,扩充这段代码来管理大量服务器将耗费很长时间。

因此我们必须放弃这段代码;我们可以建立异步连接-不需要等待fsockopen返回连接状态。PHP仍然需要解析hostname(所以直接使用ip更加明智),不过将在打开一个连接之后立刻返回,继而我们就可以连接下一台服务器。

有两种方法可以实现;PHP5中可以使用新增的stream_socket_client()函数直接替换掉fsocketopen()。PHP5之前的版本,你需要自己动手,用sockets扩展解决问题。

下面是PHP5中的解决方法:

它运行的很好,但是在fsockopen()分析完hostname并且建立一个成功的连接(或者延时$timeout秒)之前,扩充这段代码来管理大量服务器将耗费很长时间。

因此我们必须放弃这段代码;我们可以建立异步连接-不需要等待fsockopen返回连接状态。PHP仍然需要解析hostname(所以直接使用ip更加明智),不过将在打开一个连接之后立刻返回,继而我们就可以连接下一台服务器。

有两种方法可以实现;PHP5中可以使用新增的stream_socket_client()函数直接替换掉fsocketopen()。PHP5之前的版本,你需要自己动手,用sockets扩展解决问题。

 代码如下: 

<?php
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com");
$timeout = 15;
$status = array();
$sockets = array();
/* Initiate connections to all the hosts simultaneously */
foreach ($hosts as $id => $host) {
 $s = stream_socket_client("
$
$host:80", $errno, $errstr, $timeout,
 STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
 if ($s) {
 $sockets[$id] = $s;
 $status[$id] = "in progress";
 } else {
 $status[$id] = "failed, $errno $errstr";
 }
}
/* Now, wait for the results to come back in */
while (count($sockets)) {
 $read = $write = $sockets;
 /* This is the magic function - explained below */
 $n = stream_select($read, $write, $e = null, $timeout);
 if ($n > 0) {
 /* readable sockets either have data for us, or are failed
 * connection attempts */
 foreach ($read as $r) {
  $id = array_search($r, $sockets);
  $data = fread($r, 8192);
  if (strlen($data) == 0) {
 if ($status[$id] == "in progress") {
 $status[$id] = "failed to connect";
 }
 fclose($r);
 unset($sockets[$id]);
  } else {
 $status[$id] .= $data;
  }
 }
 /* writeable sockets can accept an HTTP request */
 foreach ($write as $w) {
 $id = array_search($w, $sockets);
 fwrite($w, "HEAD / HTTP/1.0rnHost: "
 . $hosts[$id] . "rnrn");
 $status[$id] = "waiting for response";
 }
 } else {
 /* timed out waiting; assume that all hosts associated
 * with $sockets are faulty */
 foreach ($sockets as $id => $s) {
 $status[$id] = "timed out " . $status[$id];
 }
 break;
 }
}
foreach ($hosts as $id => $host) {
 echo "Host: $hostn";
 echo "Status: " . $status[$id] . "nn";
} 
?>

我们用stream_select()等待sockets打开的连接事件。stream_select()调用系统的select(2)函数来工作:前面三个参数是你要使用的streams的数组;你可以对其读取,写入和获取异常(分别针对三个参数)。stream_select()可以通过设置$timeout(秒)参数来等待事件发生-事件发生时,相应的sockets数据将写入你传入的参数。

下面是PHP4.1.0之后版本的实现,如果你已经在编译PHP时包含了sockets(ext/sockets)支持,你可以使用根上面类似的代码,只是需要将上面的streams/filesystem函数的功能用ext/sockets函数实现。主要的不同在于我们用下面的函数代替stream_socket_client()来建立连接:

代码如下:

<?php
// This value is correct for Linux, other systems have other values
define('EINPROGRESS', 115);
function non_blocking_connect($host, $port, &$errno, &$errstr, $timeout) {
 $ip = gethostbyname($host);
 $s = socket_create(AF_INET, SOCK_STREAM, 0);
 if (socket_set_nonblock($s)) {
 $r = @socket_connect($s, $ip, $port);
 if ($r || socket_last_error() == EINPROGRESS) {
 $errno = EINPROGRESS;
 return $s;
 }
 }
 $errno = socket_last_error($s);
 $errstr = socket_strerror($errno);
 socket_close($s);
 return false;
}
?>

现在用socket_select()替换掉stream_select(),用socket_read()替换掉fread(),用socket_write()替换掉fwrite(),用socket_close()替换掉fclose()就可以执行脚本了!

PHP5的先进之处在于,你可以用stream_select()处理几乎所有的stream-例如你可以通过include STDIN用它接收键盘输入并保存进数组,你还可以接收通过proc_open()打开的管道中的数据。

下面来分享一个PHP多线程类

代码如下:

class thread {
 
 var $hooks = array();
 var $args = array();
 
 function thread() {
 }
 
 function addthread($func)
 {
  $args = array_slice(func_get_args(), 1);
  $this->hooks[] = $func;
  $this->args[] = $args;
  return true;
 }
 
 function runthread()
 {
  if(isset($_GET['flag']))
  {
   $flag = intval($_GET['flag']);
  }
  if($flag || $flag === 0)
  {
   call_user_func_array($this->hooks[$flag], $this->args[$flag]);
  }
  else
  {
   for($i = 0, $size = count($this->hooks); $i < $size; $i++)
   {
    $fp=fsockopen($_SERVER['HTTP_HOST'],$_SERVER['SERVER_PORT']);
    if($fp)
    {
     $out = "GET {$_SERVER['PHP_SELF']}?flag=$i HTTP/1.1rn";
     $out .= "Host: {$_SERVER['HTTP_HOST']}rn";
     $out .= "Connection: Closernrn";
     fputs($fp,$out);
     fclose($fp);
    }
   }
  }
 }
}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

PHP 相关文章推荐
rrmdir php中递归删除目录及目录下的文件
May 15 PHP
php实现用户在线时间统计详解
Oct 08 PHP
PHP动态分页函数,PHP开发分页必备啦
Nov 07 PHP
使用php get_headers 判断URL是否有效的解决办法
Apr 27 PHP
php生成图形验证码几种方法小结
Aug 15 PHP
PHP如何利用P3P实现跨域
Aug 24 PHP
PHP扩展模块Pecl、Pear以及Perl的区别
Apr 09 PHP
分享一个Laravel好用的Cache宏
Mar 02 PHP
PHP查询附近的人及其距离的实现方法
May 11 PHP
php版微信公众账号第三方管理工具开发简明教程
Sep 23 PHP
php rsa 加密,解密,签名,验签详解
Dec 06 PHP
搜索附近的人PHP实现代码
Feb 11 PHP
PHP在innodb引擎下快速代建全文搜索功能简明教程【基于xunsearch】
Oct 14 #PHP
PHP面向对象自动加载机制原理与用法分析
Oct 14 #PHP
ThinkPHP打水印及设置水印位置的方法
Oct 14 #PHP
PHP 将dataurl转成图片image方法总结
Oct 14 #PHP
php版微信公众号接口实现发红包的方法
Oct 14 #PHP
PHP版微信第三方实现一键登录及获取用户信息的方法
Oct 14 #PHP
php版银联支付接口开发简明教程
Oct 14 #PHP
You might like
PHP Ajax实现页面无刷新发表评论
2007/01/02 PHP
PHP项目开发中最常用的自定义函数整理
2010/12/02 PHP
php数组一对一替换实现代码
2012/08/31 PHP
PHP使用SOAP调用.net的WebService数据
2013/11/12 PHP
php使用wordwrap格式化文本段落的方法
2015/03/17 PHP
修复ie8&amp;chrome下window的resize事件多次执行
2011/10/20 Javascript
深入探讨JavaScript、JQuery屏蔽网页鼠标右键菜单及禁止选择复制
2014/06/10 Javascript
node.js中的fs.fsync方法使用说明
2014/12/15 Javascript
JavaScript实现仿淘宝商品购买数量的增减效果
2016/01/22 Javascript
基于BootStrap Metronic开发框架经验小结【五】Bootstrap File Input文件上传插件的用法详解
2016/05/12 Javascript
浅谈jQuery this和$(this)的区别及获取$(this)子元素对象的方法
2016/11/29 Javascript
关于Vue.js一些问题和思考学习笔记(2)
2016/12/02 Javascript
nodejs Assert中equal(),strictEqual(),deepEqual(),strictDeepEqual()比较
2017/09/18 NodeJs
不到200行 JavaScript 代码实现富文本编辑器的方法
2018/01/03 Javascript
SVG实现时钟效果
2018/07/17 Javascript
jQuery.parseJSON()函数详解
2019/02/28 jQuery
JS实现动态添加外部js、css到head标签的方法
2019/06/05 Javascript
[11:27]《一刀刀一天》之DOTA全时刻20:TI4总奖金突破920W TS赛事分析
2014/06/18 DOTA
[04:29]DOTA2亚洲邀请赛小组赛第一日 TOP10精彩集锦
2015/02/01 DOTA
教你安装python Django(图文)
2013/11/04 Python
Python中Scrapy爬虫图片处理详解
2017/11/29 Python
解决Django数据库makemigrations有变化但是migrate时未变动问题
2018/05/30 Python
Python3中的bytes和str类型详解
2019/05/02 Python
python实现的读取网页并分词功能示例
2019/10/29 Python
Python爬虫逆向分析某云音乐加密参数的实例分析
2020/12/04 Python
详解基于Facecognition+Opencv快速搭建人脸识别及跟踪应用
2021/01/21 Python
CSS3的一个简单导航栏实现
2015/08/03 HTML / CSS
Perfume’s Club澳大利亚官网:西班牙领先的在线美容店
2021/02/01 全球购物
error和exception有什么区别
2012/10/02 面试题
药学专业大学生个人的自我评价
2013/11/04 职场文书
学习党课思想汇报
2013/12/29 职场文书
学校端午节活动方案
2014/08/23 职场文书
2015年元宵节活动总结
2015/02/06 职场文书
Nginx+Tomcat负载均衡集群的实现示例
2021/10/24 Servers
Redis特殊数据类型bitmap位图
2022/06/01 Redis
nginx访问报403错误的几种情况详解
2022/07/23 Servers