如何解决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数据库连接类~~做成一个分页类!
Nov 25 PHP
PHP 读取和修改大文件的某行内容的代码
Oct 30 PHP
用PHP将网址字符串转换成超链接(网址或email)
May 25 PHP
php页面缓存ob系列函数介绍
Oct 18 PHP
解析PHP处理换行符的问题 \r\n
Jun 13 PHP
PHP 使用pcntl和libevent 实现Timer功能
Oct 27 PHP
使用php转义输出HTML到JavaScript
Mar 27 PHP
linux下php上传文件注意事项
Jun 11 PHP
CodeIgniter框架常见用法工作总结
Mar 16 PHP
ThinkPHP3.2框架使用addAll()批量插入数据的方法
Mar 16 PHP
PHP调用API接口实现天气查询功能的示例
Sep 21 PHP
PHP基于redis计数器类定义与用法示例
Feb 08 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
《星际争霸II》全新指挥官斯台特曼现已上线
2020/03/08 星际争霸
PHP 程序员的调试技术小结
2009/11/15 PHP
如何使用PHP计算上一个月的今天
2013/05/23 PHP
php不用正则验证真假身份证
2013/11/06 PHP
PHP使用glob函数遍历目录或文件夹的方法
2014/12/16 PHP
html数组字符串拼接的最快方法
2009/09/16 Javascript
js控制table合并具体实现
2014/02/20 Javascript
javascript实现修改微信分享的标题内容等
2014/12/11 Javascript
jQuery页面元素动态添加后绑定事件丢失方法,非 live
2016/06/16 Javascript
jQuery ajaxForm()的应用
2016/10/14 Javascript
Bootstrap学习笔记 轮播(Carousel)插件
2017/03/21 Javascript
Angular 4.x+Ionic3踩坑之Ionic 3.x界面传值详解
2018/03/13 Javascript
微信小程序吸底区域适配iPhoneX的实现
2020/04/09 Javascript
基于JS实现table导出Excel并保留样式
2020/05/19 Javascript
javascript实现数字时钟效果
2021/02/06 Javascript
[48:48]完美世界DOTA2联赛PWL S3 Magama vs GXR 第一场 12.19
2020/12/24 DOTA
利用python发送和接收邮件
2016/09/27 Python
python中is与双等于号“==”的区别示例详解
2017/11/21 Python
Python中字符串与编码示例代码
2019/05/20 Python
django settings.py 配置文件及介绍
2019/07/15 Python
Django 静态文件配置过程详解
2019/07/23 Python
python selenium操作cookie的实现
2020/03/18 Python
Windows下Anaconda和PyCharm的安装与使用详解
2020/04/23 Python
python爬虫智能翻页批量下载文件的实例详解
2021/02/02 Python
小学爱国卫生月活动总结
2014/06/30 职场文书
化学专业毕业生求职信
2014/07/28 职场文书
招标授权委托书样本
2014/09/23 职场文书
公司离职证明标准格式
2014/11/18 职场文书
2015年信息技术教研组工作总结
2015/07/22 职场文书
关于军训的感想
2015/08/07 职场文书
先进党支部事迹材料2016
2016/02/26 职场文书
你对自己的信用报告有过了解吗?
2019/07/09 职场文书
SQL Server 数据库实验课第五周——常用查询条件
2021/04/05 SQL Server
一看就懂的MySQL的聚簇索引及聚簇索引是如何长高的
2021/05/25 MySQL
Python中OpenCV实现查找轮廓的实例
2021/06/08 Python
漫画「你在春天醒来」第10卷封面公开
2022/03/21 日漫