php多线程实现方法及用法实例详解


Posted in PHP onOctober 26, 2015

下面我们来介绍具体php多线程实现程序代码,有需要了解的同学可参考。
当有人想要实现并发功能时,他们通常会想到用fork或者spawn threads,但是当他们发现php不支持多线程的时候,大概会转换思路去用一些不够好的语言,比如perl。
其实的是大多数情况下,你大可不必使用fork 或者线程,并且你会得到比用fork 或thread 更好的性能。
假设你要建立一个服务来检查正在运行的n台服务器,以确定他们还在正常运转。你可能会写下面这样的代码:
代码如下 

<?php
$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中的解决方法:
代码如下

<?php
$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()来建立连接:
代码如下 

<?php
// 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多线程类
代码如下

* @title:   PHP多线程类(Thread)
 * @version:  1.0
 * 
 * PHP多线程应用示例:
 * require_once 'thread.class.php';
 * $thread = new thread();
 * $thread->addthread('action_log','a');
 * $thread->addthread('action_log','b');
 * $thread->addthread('action_log','c');
 * $thread->runthread();
 * 
 * function action_log($info) {
 *   $log = 'log/' . microtime() . '.log';
 *   $txt = $info . "rnrn" . 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn";
 *   $fp = fopen($log, 'w');
 *   fwrite($fp, $txt);
 *   fclose($fp);
 * }
 */
class thread {
 
  var $hooks = array();
  var $args = array();
 
  function thread() {
  }
 
  function addthread($func)
  {
    $args = array_slice(func_get_args(), 1);
    $this->hooks[] = $func;
    $this->args[] = $args;
    return true;
  }
 
  function runthread()
  {
    if(isset($_GET['flag']))
    {
      $flag = intval($_GET['flag']);
    }
    if($flag || $flag === 0)
    {
      call_user_func_array($this->hooks[$flag], $this->args[$flag]);
    }
    else
    {
      for($i = 0, $size = count($this->hooks); $i < $size; $i++)
      {
        $fp=fsockopen($_SERVER['HTTP_HOST'],$_SERVER['SERVER_PORT']);
        if($fp)
        {
          $out = "GET {$_SERVER['PHP_SELF']}?flag=$i HTTP/1.1rn";
          $out .= "Host: {$_SERVER['HTTP_HOST']}rn";
          $out .= "Connection: Closernrn";
          fputs($fp,$out);
          fclose($fp);
        }
      }
    }
  }
}

希望本文所述对大家的PHP程序设计有所帮助。

PHP 相关文章推荐
一个高ai的分页函数和一个url函数
Oct 09 PHP
一个取得文件扩展名的函数
Oct 09 PHP
一个可以删除字符串中HTML标记的PHP函数
Oct 09 PHP
利用discuz自带通行证整合dedecms的方法以及文件下载
Mar 06 PHP
php Try Catch异常测试
Mar 01 PHP
PHP获取当前文件所在目录 getcwd()函数
May 13 PHP
PHP学习笔记之一
Jan 17 PHP
PHP安全配置详细说明
Sep 26 PHP
浅析php中抽象类和接口的概念以及区别
Jun 27 PHP
php递归法读取目录及文件的方法
Jan 30 PHP
浅析PHP关键词替换的类(避免重复替换,保留与还原原始链接)
Sep 22 PHP
PHP实现mysqli批量执行多条语句的方法示例
Jul 22 PHP
浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
Oct 26 #PHP
PHP和C#可共用的可逆加密算法详解
Oct 26 #PHP
日常整理PHP中简单的图形处理(经典)
Oct 26 #PHP
php 参数过滤、数据过滤详解
Oct 26 #PHP
php解析url并得到url中的参数及获取url参数的四种方式
Oct 26 #PHP
php实现CSV文件导入和导出
Oct 24 #PHP
PHP错误Warning:mysql_query()解决方法
Oct 24 #PHP
You might like
PHP用mb_string函数库处理与windows相关中文字符及Win环境下开启PHP Mb_String方法
2015/11/11 PHP
PHP实现的链式队列结构示例
2017/09/15 PHP
thinkPHP5实现数据库添加内容的方法
2017/10/25 PHP
Ucren Virtual Desktop V2.0
2006/11/07 Javascript
Jquery Ajax请求代码(2)
2011/01/07 Javascript
jQuery+JSON+jPlayer实现QQ空间音乐查询功能示例
2013/06/17 Javascript
基于jQuery日历插件制作日历
2016/03/11 Javascript
jQuery zTree树插件简单使用教程
2017/01/10 Javascript
Jquery与Bootstrap实现后台管理页面增删改查功能示例
2017/01/22 Javascript
AngularJS实现自定义指令与控制器数据交互的方法示例
2017/06/19 Javascript
jQuery封装placeholder效果实现方法,让低版本浏览器支持该效果
2017/07/08 jQuery
[42:06]2019国际邀请赛全明星赛 8.23
2019/09/05 DOTA
python图像处理之反色实现方法
2015/05/30 Python
Python抓取淘宝下拉框关键词的方法
2015/07/08 Python
深入解析Python中的list列表及其切片和迭代操作
2016/03/13 Python
深入理解 Python 中的多线程 新手必看
2016/11/20 Python
python爬虫自动创建文件夹的功能
2018/08/01 Python
Numpy之文件存取的示例代码
2018/08/03 Python
python如何读取bin文件并下发串口
2019/07/05 Python
Python代理IP爬虫的新手使用教程
2019/09/05 Python
解决Keras 与 Tensorflow 版本之间的兼容性问题
2020/02/07 Python
解决django xadmin主题不显示和只显示bootstrap2的问题
2020/03/30 Python
Python Opencv实现单目标检测的示例代码
2020/09/08 Python
python 实现弹球游戏的示例代码
2020/11/17 Python
英国一家专门出售品牌鞋子的网站:Allsole
2016/08/07 全球购物
迪士尼西班牙官方网上商店:ShopDisney西班牙
2020/02/02 全球购物
科颜氏印度官网:Kiehl’s印度
2021/02/20 全球购物
同步和异步有何异同,在什么情况下分别使用他们?举例说明
2014/02/27 面试题
物理学专业自荐信
2014/06/11 职场文书
2014年小学生迎国庆65周年演讲稿
2014/09/27 职场文书
2016年大学校运会广播稿件
2015/12/21 职场文书
2016年先进教师个人事迹材料
2016/02/26 职场文书
教学工作总结范文5篇
2019/08/19 职场文书
2019年二手房买卖合同范本
2019/10/14 职场文书
Python基于Tkinter开发一个爬取B站直播弹幕的工具
2021/05/06 Python
错误码NET::ERR_CERT_DATE_INVALID证书已过期解决方法?
2022/07/07 数码科技