如何解决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
攻克CakePHP系列三 表单数据增删改
Oct 22 PHP
解析PHP中如何将数组变量写入文件
Jun 06 PHP
windows7下安装php的imagick和imagemagick扩展教程
Jul 04 PHP
PHP实现Javascript中的escape及unescape函数代码分享
Feb 10 PHP
PHP入门教程之表单与验证实例详解
Sep 11 PHP
thinkPHP简单实现多个子查询语句的方法
Dec 05 PHP
php使用Jpgraph创建柱状图展示年度收支表效果示例
Feb 15 PHP
浅析PHP数据导出知识点
Feb 17 PHP
PHP getNamespaces()函数讲解
Feb 03 PHP
微信公众平台开发教程④ ThinkPHP框架下微信支付功能图文详解
Apr 10 PHP
PHP设计模式(八)装饰器模式Decorator实例详解【结构型】
May 02 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 MsSql server时遇到的中文编码问题
2009/06/11 PHP
解析zend Framework如何自动加载类
2013/06/28 PHP
php将session放入memcached的设置方法
2014/02/14 PHP
PHP+MySQL插入操作实例
2015/01/21 PHP
Yii2中添加全局函数的方法分析
2017/05/04 PHP
php对xml文件的增删改查操作实现方法分析
2017/05/19 PHP
php调用云片网接口发送短信的实现方法
2017/10/25 PHP
gearman管理工具GearmanManager的安装与php使用方法示例
2020/02/27 PHP
Javascript实现带关闭按钮的网页漂浮广告代码
2014/01/12 Javascript
js动态创建标签示例代码
2014/06/09 Javascript
jQuery中:disabled选择器用法实例
2015/01/04 Javascript
jQuery Ajax调用WCF服务详细教程
2015/03/31 Javascript
Eclipse引入jquery报错如何解决
2015/12/01 Javascript
理解js对象继承的N种模式
2016/01/25 Javascript
jQuery实现无限往下滚动效果代码
2016/04/16 Javascript
jQuery插件datatables使用教程
2016/04/21 Javascript
巧用jQuery选择器提高写表单效率的方法
2016/08/19 Javascript
用AngularJS的指令实现tabs切换效果
2016/08/31 Javascript
关于js原型的面试题讲解
2016/09/25 Javascript
jquery精度计算代码 jquery指定精确小数位
2017/02/06 Javascript
vue父子组件的嵌套的示例代码
2017/09/08 Javascript
Django与Vue语法的冲突问题完美解决方法
2017/12/14 Javascript
angularJs 表格添加删除修改查询方法
2018/02/27 Javascript
vue2.0使用swiper组件实现轮播的示例代码
2018/03/03 Javascript
基于vue-video-player自定义播放器的方法
2018/03/21 Javascript
微信小程序实现横向增长表格的方法
2018/07/24 Javascript
Vue项目打包部署到iis服务器的配置方法
2019/10/14 Javascript
python实现机器人卡牌
2019/10/06 Python
pytorch使用 to 进行类型转换方式
2020/01/08 Python
细说NumPy数组的四种乘法的使用
2020/12/18 Python
[原创]赚疯了!转手立赚800+?大佬的python「抢茅台脚本」使用教程
2021/01/12 Python
英国户外服装品牌:Craghoppers
2019/04/25 全球购物
社团活动策划书范文
2014/01/09 职场文书
公司表扬稿范文
2015/05/05 职场文书
2016入党积极分子心得体会
2016/01/06 职场文书
幼儿园六一儿童节开幕词
2016/03/04 职场文书