如何解决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中一个有意思的日期逻辑处理
Mar 25 PHP
php class中self,parent,this的区别以及实例介绍
Apr 24 PHP
Codeigniter实现多文件上传并创建多个缩略图
Jun 12 PHP
php开启与关闭错误提示适用于没有修改php.ini的权限
Oct 16 PHP
一个简单的php路由类
May 29 PHP
php断点续传之文件分割合并详解
Dec 13 PHP
Yii2下session跨域名共存的解决方案
Feb 04 PHP
基于thinkPHP3.2实现微信接入及查询token值的方法
Apr 18 PHP
php使用 readfile() 函数设置文件大小大小的方法
Aug 11 PHP
PHP操作MongoDB实现增删改查功能【附php7操作MongoDB方法】
Apr 24 PHP
PHP简单实现图片格式转换(jpg转png,gif转png等)
Oct 30 PHP
php使用goto实现自动重启swoole、reactphp、workerman服务的代码
Apr 13 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 file_get_contents 函数超时的几种解决方法
2009/07/30 PHP
php获取后台Job管理的实现代码
2011/06/10 PHP
php跨站攻击实例分析
2014/10/28 PHP
thinkPHP框架实现类似java过滤器的简单方法示例
2018/09/05 PHP
Thinkphp 5.0实现微信企业付款到零钱
2018/09/30 PHP
解决PhpStorm64不能启动的问题
2020/06/20 PHP
ie 调试javascript的工具
2009/04/29 Javascript
js的一些常用方法小结
2011/06/29 Javascript
编写js扩展方法判断一个数组中是否包含某个元素
2013/11/08 Javascript
js动态控制table的tr、td增加及删除的具体实现
2014/04/30 Javascript
使用Node.js为其他程序编写扩展的基本方法
2015/06/23 Javascript
理解JavaScript中worker事件api
2015/12/25 Javascript
jquery zTree异步加载、模糊搜索简单实例分享
2016/03/24 Javascript
jQuery基于扩展实现的倒计时效果
2016/05/14 Javascript
微信小程序组件 contact-button(客服会话按钮)详解及实例代码
2017/01/10 Javascript
vue引入swiper插件的使用实例
2017/07/19 Javascript
javascript+html5+css3自定义弹出窗口效果
2017/10/26 Javascript
详解ES6 系列之异步处理实战
2018/10/26 Javascript
对TypeScript库进行单元测试的方法
2019/07/18 Javascript
js+canvas实现纸牌游戏
2020/03/16 Javascript
JavaScript动态生成表格的示例
2020/11/02 Javascript
详解如何在vue+element-ui的项目中封装dialog组件
2020/12/11 Vue.js
[45:18]完美世界DOTA2联赛循环赛 PXG vs IO 第二场 11.06
2020/11/09 DOTA
Django实现登录随机验证码的示例代码
2018/06/20 Python
Caffe均值文件mean.binaryproto转mean.npy的方法
2018/07/09 Python
Pycharm中配置远程Docker运行环境的教程图解
2020/06/11 Python
从Pytorch模型pth文件中读取参数成numpy矩阵的操作
2021/03/04 Python
应届生文秘专业个人自荐信格式
2013/09/21 职场文书
水电工岗位职责
2014/02/12 职场文书
影子教师研修方案
2014/06/14 职场文书
2014年办公室个人工作总结
2014/11/12 职场文书
幼儿园教师个人总结
2015/02/05 职场文书
2019财务管理制度最新范本!
2019/07/09 职场文书
MySQL系列之十四 MySQL的高可用实现
2021/07/02 MySQL
python中的3种定义类方法
2021/11/27 Python
服务器nginx权限被拒绝解决案例
2022/09/23 Servers