如何解决PHP无法实现多线程的问题


Posted in PHP onSeptember 25, 2015

有没有办法在PHP中实现多线程呢?假设你正在写一个基于多台服务器的PHP应用,理想的情况时同时向多台服务器发送请求,而不是一台接一台。可以实现吗?当有人想要实现并发功能时,他们通常会想到用fork或者spawn threads,但是当他们发现PHP不支持多线程的时候,大概会转换思路去用一些不够好的语言,比如Perl。
假设你要建立一个服务来检查正在运行的n台服务器,以确定他们还在正常运转。你可能会写下面这样的代码:

$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中的解决方法:

$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()来建立连接:

// 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无法实现多线程的问题。

PHP 相关文章推荐
PHP默认安装产生系统漏洞
Oct 09 PHP
php基础知识:类与对象(3) 构造函数和析构函数
Dec 13 PHP
DEDE采集大师官方留后门的删除办法
Jan 08 PHP
求PHP数组最大值,最小值的代码
Oct 31 PHP
php文件打包 下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
Jun 13 PHP
关于UEditor编辑器远程图片上传失败的解决办法
Aug 31 PHP
php设置session值和cookies的学习示例
Mar 21 PHP
php中smarty实现多模版网站的方法
Jun 11 PHP
PHP针对多用户实现更换头像功能
Sep 04 PHP
PHP保留两位小数的几种方法
Jul 24 PHP
thinkPHP3.2使用RBAC实现权限管理的实现
Aug 27 PHP
PHP中将一个字符串部分字符用星号*替代隐藏的实现代码
Sep 08 PHP
PHP网站建设的流程与步骤分享
Sep 25 #PHP
ThinkPHP进程计数类Process用法实例详解
Sep 25 #PHP
php实现的Curl封装类Curl.class.php用法实例分析
Sep 25 #PHP
php实现的微信红包算法分析(非官方)
Sep 25 #PHP
PHP简单实现断点续传下载的方法
Sep 25 #PHP
分享3个php获取日历的函数
Sep 25 #PHP
PHP中配置IIS7实现基本身份验证的方法
Sep 24 #PHP
You might like
php Try Catch异常测试
2009/03/01 PHP
解析php DOMElement 操作xml 文档的实现代码
2013/05/10 PHP
php rsa 加密,解密,签名,验签详解
2016/12/06 PHP
php双层循环(九九乘法表)
2017/10/23 PHP
Laravel 5.1 框架Blade模板引擎用法实例分析
2020/01/04 PHP
json跟xml的对比分析
2008/06/10 Javascript
JavaScript对象、属性、事件手册集合方便查询
2010/07/04 Javascript
IE8下Jquery获取select选中的值post到后台报错问题
2014/07/02 Javascript
JavaScript如何实现组合列表框中元素移动效果
2016/03/01 Javascript
javascript基本算法汇总
2016/03/09 Javascript
基于jQuery实现动态搜索显示功能
2016/05/05 Javascript
JavaScript中的原型继承基础学习教程
2016/05/06 Javascript
Vue实现自定义下拉菜单功能
2018/07/16 Javascript
Bootstrap 模态框自定义点击和关闭事件详解
2018/08/10 Javascript
解决Vue axios post请求,后台获取不到数据的问题方法
2018/08/11 Javascript
小程序指纹验证的实现代码
2018/12/04 Javascript
微信小程序环境下将文件上传到OSS的方法步骤
2019/05/31 Javascript
layuiAdmin循环遍历展示商品图片列表的方法
2019/09/16 Javascript
layui禁用侧边导航栏点击事件的解决方法
2019/09/25 Javascript
Javascript作用域和作用域链原理解析
2020/03/03 Javascript
Jquery如何使用animation动画效果改变背景色的代码
2020/07/20 jQuery
Django URL传递参数的方法总结
2016/08/28 Python
python筛选出两个文件中重复行的方法
2018/05/31 Python
Flask之flask-script模块使用
2018/07/26 Python
Python Django实现layui风格+django分页功能的例子
2019/08/29 Python
Python实现密码薄文件读写操作
2019/12/16 Python
Pyspark读取parquet数据过程解析
2020/03/27 Python
python绘图模块之利用turtle画图
2021/02/12 Python
用缩写的指针比较"if(p)" 检查空指针是否可靠?如果空指针的内部表达不是0会怎么样?
2014/01/05 面试题
外科实习自我鉴定
2013/10/06 职场文书
中学生自我鉴定
2014/02/04 职场文书
农民工工资发放承诺书
2014/03/31 职场文书
感谢信范文大全
2015/01/23 职场文书
高中物理教学反思
2016/02/19 职场文书
2019年二手房买卖合同范本
2019/10/14 职场文书
详解Laravel制作API接口
2021/05/31 PHP