如何解决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 相关文章推荐
解决控件遮挡问题:关于有窗口元素和无窗口元素
Jan 28 PHP
PHP SEO优化之URL优化方法
Apr 21 PHP
让Json更懂中文(JSON_UNESCAPED_UNICODE)
Oct 27 PHP
遍历指定目录下的所有目录和文件的php代码
Nov 27 PHP
国外PHP程序员的13个好习惯小结
Feb 20 PHP
PHP实现的简易版图片相似度比较
Jan 07 PHP
PHP实现从远程下载文件的方法
Mar 12 PHP
PHP获取数组最大值下标的方法
May 12 PHP
PHP简单生成缩略图相册的方法
Jul 29 PHP
完美利用Yii2微信后台开发的系列总结
Jul 18 PHP
php微信公众平台开发(四)回复功能开发
Dec 06 PHP
解决php 处理 form 表单提交多个 name 属性值相同的 input 标签问题
May 11 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
WHOIS类的修改版
2006/10/09 PHP
关于js与php互相传值的介绍
2013/06/25 PHP
如何修改和添加Apache的默认站点目录
2013/07/05 PHP
php判断用户是否关注微信公众号
2016/07/22 PHP
PHP实现时间日期友好显示实现代码
2019/09/08 PHP
PHP高并发和大流量解决方案整理
2019/12/24 PHP
innerhtml用法 innertext用法 以及innerHTML与innertext的区别
2009/10/26 Javascript
关于JavaScript的with 语句的使用方法
2011/05/09 Javascript
Jquery each方法跳出循环,并获取返回值(实例讲解)
2013/12/12 Javascript
javascript随机之洗牌算法深入分析
2014/06/07 Javascript
jQuery UI插件自定义confirm确认框的方法
2015/03/20 Javascript
Nodejs实战心得之eventproxy模块控制并发
2015/10/27 NodeJs
JS实现n秒后自动跳转的两种方法
2020/11/30 Javascript
使用开源工具制作网页验证码的方法
2016/10/17 Javascript
Bootstrap基本样式学习笔记之按钮(4)
2016/12/07 Javascript
Angular2数据绑定详解
2017/04/18 Javascript
Textarea输入字数限制实例(兼容iOS&安卓)
2017/07/06 Javascript
ES6 对象的新功能与解构赋值介绍
2019/02/05 Javascript
js获取对象,数组所有属性键值(key)和对应值(value)的方法示例
2019/06/19 Javascript
JS表单验证插件之数据与逻辑分离操作实例分析【策略模式】
2020/05/01 Javascript
Python使用metaclass实现Singleton模式的方法
2015/05/05 Python
python获取局域网占带宽最大3个ip的方法
2015/07/09 Python
python 容器总结整理
2017/04/04 Python
利用python求相邻数的方法示例
2017/08/18 Python
python爬虫之模拟登陆csdn的实例代码
2018/05/18 Python
利用python批量爬取百度任意类别的图片的实现方法
2020/10/07 Python
Python实现迪杰斯特拉算法并生成最短路径的示例代码
2020/12/01 Python
Python中正则表达式对单个字符,多个字符和匹配边界等使用
2021/01/27 Python
CSS实现圆形放大镜狙击镜效果 只有圆圈里的放大
2012/12/10 HTML / CSS
同步和异步有何异同,在什么情况下分别使用他们?举例说明
2014/02/27 面试题
校运会口号
2014/06/18 职场文书
博士生求职信
2014/07/06 职场文书
工程索赔意向书
2014/08/30 职场文书
2015年护士节活动总结
2015/02/10 职场文书
史上最全的军训拉歌口号
2015/12/25 职场文书
《亲亲我的妈妈》观后感(3篇)
2019/09/26 职场文书