如何解决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 配置文件中open_basedir选项作用
Jul 19 PHP
php下批量挂马和批量清马代码
Feb 27 PHP
114啦源码(114la)不能生成地方房产和地方报刊问题4级页面0字节的解决方法
Jan 12 PHP
zf框架的校验器使用使用示例(自定义校验器和校验器链)
Mar 13 PHP
laravel 4安装及入门图文教程
Oct 29 PHP
php数组合并与拆分实例分析
Jun 12 PHP
PHP生成随机密码方法汇总
Aug 27 PHP
php实现常见图片格式的水印和缩略图制作(面向对象)
Jun 15 PHP
php版微信公众平台开发之验证步骤实例详解
Sep 23 PHP
PHP批量删除jQuery操作
Jul 23 PHP
PHP JWT初识及其简单示例
Oct 10 PHP
php实现映射操作实例详解
Oct 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
星际争霸中的热键
2020/03/04 星际争霸
php download.php实现代码 跳转到下载文件(response.redirect)
2009/08/26 PHP
一个PHP的ZIP压缩类分享
2014/05/04 PHP
详解配置 Apache 服务器支持 PHP 文件的解析
2017/02/15 PHP
gearman中任务的优先级和返回状态实例分析
2020/02/27 PHP
JavaScript XML实现两级级联下拉列表
2008/11/10 Javascript
JavaScript 拖拉缩放效果
2008/12/10 Javascript
ext checkboxgroup 回填数据解决
2009/08/21 Javascript
js 未结束的字符串常量错误解决方法
2010/06/13 Javascript
自己写的兼容ie和ff的在线文本编辑器类似ewebeditor
2012/12/12 Javascript
JS实现将人民币金额转换为大写的示例代码
2014/02/13 Javascript
js获取url中的参数且参数为中文时通过js解码
2014/03/19 Javascript
浅谈Angular的$q, defer, promise
2016/12/20 Javascript
JS实现的二叉树算法完整实例
2017/04/06 Javascript
JS 组件系列之 bootstrap treegrid 组件封装过程
2017/04/28 Javascript
jQuery实现多张图片上传预览(不经过后端处理)
2017/04/29 jQuery
详解vue-meta如何让你更优雅的管理头部标签
2018/01/18 Javascript
浅谈vue.use()方法从源码到使用
2019/05/12 Javascript
微信小程序云开发如何实现数据库自动备份实现
2019/08/16 Javascript
vue-cli随机生成port源码的方法
2019/09/02 Javascript
node.js中process进程的概念和child_process子进程模块的使用方法示例
2020/02/11 Javascript
python中黄金分割法实现方法
2015/05/06 Python
Python fileinput模块使用实例
2015/05/28 Python
python实现RSA加密(解密)算法
2016/02/17 Python
Python实现自定义顺序、排列写入数据到Excel的方法
2018/04/23 Python
利用python3 的pygame模块实现塔防游戏
2019/12/30 Python
任意一块网页内容实现“活”的背景(目前火狐浏览器专有)
2014/05/07 HTML / CSS
Dyson戴森波兰官网:Dyson.pl
2019/08/05 全球购物
行政前台岗位职责
2013/12/04 职场文书
个人租房协议书(范本)
2014/10/14 职场文书
2014年扶贫帮困工作总结
2014/12/09 职场文书
解决mysql模糊查询索引失效问题的几种方法
2021/06/18 MySQL
Python 发送SMTP邮件的简单教程
2021/06/24 Python
mysql5.7的安装及Navicate长久免费使用的实现过程
2021/11/17 MySQL
Netty分布式客户端接入流程初始化源码分析
2022/03/25 Java/Android
Android开发EditText禁止输入监听及InputFilter字符过滤
2022/06/10 Java/Android