如何解决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与MySQL开发中页面出现乱码的一种解决方法
Jul 29 PHP
PHP 加密/解密函数 dencrypt(动态密文,带压缩功能,支持中文)
Jan 30 PHP
php数组函数序列之each() - 获取数组当前内部指针所指向元素的键名和键值,并将指针移到下一位
Oct 31 PHP
php写的带缓存数据功能的mysqli类
Sep 06 PHP
PHP文章按日期(月日)SQL归档语句
Nov 29 PHP
PHP读取数据库并按照中文名称进行排序实现代码
Jan 29 PHP
PHP创建word文档的方法(平台无关)
Mar 29 PHP
微信红包随机生成算法php版
Jul 21 PHP
浅谈Laravel核心解读之Console内核
Dec 02 PHP
关于Curl在Swoole协程中的解决方案详析
Sep 12 PHP
layui数据表格自定义每页条数limit设置
Oct 26 PHP
thinkphp框架类库扩展操作示例
Nov 26 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
地球防卫队:陪着奥特曼打小怪兽的人类力量 那些经典队服
2020/03/08 日漫
PHP安全性漫谈
2012/06/28 PHP
PHP关于IE下的iframe跨域导致session丢失问题解决方法
2013/10/10 PHP
PHP中preg_match正则匹配中的/u、/i、/s含义
2015/04/17 PHP
laravel框架使用阿里云短信发送消息操作示例
2020/02/15 PHP
JavaScript判断两种格式的输入日期的正确性的代码
2007/03/25 Javascript
javascript学习笔记(五)正则表达式
2011/04/08 Javascript
轻松搞定jQuery.noConflict()
2016/02/15 Javascript
探究JavaScript中的五种事件处理程序方式
2016/12/07 Javascript
关于vue.js v-bind 的一些理解和思考
2017/06/06 Javascript
js判断节假日实例代码
2017/12/27 Javascript
[59:15]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第一场 11.20
2020/11/20 DOTA
python 多线程应用介绍
2012/12/19 Python
Python版微信红包分配算法
2015/05/04 Python
Python删除windows垃圾文件的方法
2015/07/14 Python
python数据结构学习之实现线性表的顺序
2018/09/28 Python
python下载微信公众号相关文章
2019/02/26 Python
Pythony运维入门之Socket网络编程详解
2019/04/15 Python
Python3.5内置模块之random模块用法实例分析
2019/04/26 Python
python实现数据清洗(缺失值与异常值处理)
2019/12/02 Python
Python爬虫爬取百度搜索内容代码实例
2020/06/05 Python
Python如何对XML 解析
2020/06/28 Python
amazeui树节点自动展开折叠面板并选中第一个树节点的实现
2020/08/24 HTML / CSS
日本著名的服饰鞋帽综合类购物网站:MAGASEEK
2019/01/09 全球购物
Ajax请求总共有多少种Callback
2016/07/17 面试题
大学生创业计划书的范文
2014/01/07 职场文书
本科生的职业生涯规划范文
2014/01/09 职场文书
教堂婚礼主持词
2014/03/14 职场文书
经贸专业毕业生求职信范文
2014/05/01 职场文书
查摆问题自我剖析材料
2014/08/18 职场文书
暑假学习心得体会
2014/09/02 职场文书
群众路线领导干部个人对照检查材料(集锦)
2014/09/23 职场文书
2015年幼儿园保育工作总结
2015/05/12 职场文书
彻底理解golang中什么是nil
2021/04/29 Golang
Python实现生活常识解答机器人
2021/06/28 Python
详解Redis的三种常用的缓存读写策略步骤
2022/05/06 Redis