如何解决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中static静态变量的使用方法详解
Jun 04 PHP
基于initPHP的框架介绍
Apr 18 PHP
PHP Global变量定义当前页面的全局变量实现探讨
Jun 05 PHP
php删除数组元素示例分享
Feb 17 PHP
可以保证单词完整性的PHP英文字符串截取代码分享
Jul 15 PHP
phpstorm编辑器乱码问题解决
Dec 01 PHP
yii权限控制的方法(三种方法)
Dec 28 PHP
Zend Framework入门教程之Zend_View组件用法示例
Dec 09 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
Jul 20 PHP
PHP实现模拟http请求的方法分析
Dec 20 PHP
PHP排序算法之直接插入排序(Straight Insertion Sort)实例分析
Apr 20 PHP
PHP7内核CGI与FastCGI详解
Apr 14 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
一道求$b相对于$a的相对路径的php代码
2010/08/08 PHP
PHP冒泡排序算法代码详细解读
2011/07/17 PHP
PHP和.net中des加解密的实现方法
2013/02/27 PHP
destoon实现VIP排名一直在前面排序的方法
2014/08/21 PHP
10个php函数实用却不常见
2015/10/13 PHP
CodeIgniter读写分离实现方法详解
2016/01/20 PHP
php实现异步将远程链接上内容(图片或内容)写到本地的方法
2016/11/30 PHP
PHP实现使用DOM将XML数据存入数组的方法示例
2017/09/27 PHP
jQuery获取iframe的document对象的方法
2014/10/10 Javascript
JavaScript DOM进阶方法
2015/04/13 Javascript
AngularJS 指令的交互详解及实例代码
2016/09/14 Javascript
扩展bootstrap的modal模态框-动态添加modal框-弹出多个modal框
2017/02/21 Javascript
AngularJS中的拦截器实例详解
2017/04/07 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
2019/01/27 Javascript
vue elementUI 表单校验的实现代码(多层嵌套)
2019/11/06 Javascript
ant-design-vue 实现表格内部字段验证功能
2019/12/16 Javascript
Vue循环中多个input绑定指定v-model实例
2020/08/31 Javascript
为何人工智能(AI)首选Python?读完这篇文章你就知道了(推荐)
2019/04/06 Python
python 画出使用分类器得到的决策边界
2019/08/21 Python
Python collections中的双向队列deque简单介绍详解
2019/11/04 Python
使用Python函数进行模块化的实现
2019/11/15 Python
Python终端输出彩色字符方法详解
2020/02/11 Python
实现Python3数组旋转的3种算法实例
2020/09/16 Python
Python读写csv文件流程及异常解决
2020/10/20 Python
使用简单的CSS3属性实现炫酷读者墙效果
2014/01/08 HTML / CSS
CSS3 transforms应用于背景图像的解决方法
2019/04/16 HTML / CSS
英文翻译的自我评价语句
2013/10/04 职场文书
仓库理货员岗位职责
2013/12/18 职场文书
社团活动总结
2014/04/28 职场文书
建筑工程质量通病防治方案
2014/06/08 职场文书
如何写股份合作协议书
2014/09/11 职场文书
贯彻落实“八项规定”思想汇报
2014/09/13 职场文书
担保公司2015年终工作总结
2015/10/14 职场文书
个人工作失误的保证书怎么写?
2019/06/21 职场文书
Python爬取科目四考试题库的方法实现
2021/03/30 Python
微信小程序实现录音Record功能
2021/05/09 Javascript