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 相关文章推荐
Linux下ZendOptimizer的安装与配置方法
Apr 12 PHP
PHP获取网卡地址的代码
Apr 09 PHP
用PHP程序实现支持页面后退的两种方法
Jun 30 PHP
学习discuz php 引入文件的方法DISCUZ_ROOT
Jun 21 PHP
PHP生成网页快照 不用COM不用扩展.
Feb 11 PHP
PHP 将逗号、空格、回车分隔的字符串转换为数组的函数
Jun 07 PHP
PHP判断文件是否存在、是否可读、目录是否存在的代码
Oct 03 PHP
PHP的Yii框架中Model模型的学习教程
Mar 29 PHP
php中通用的excel导出方法实例
Dec 30 PHP
PHP给源代码加密的几种方法汇总(推荐)
Feb 06 PHP
ThinkPHP5.0框架验证码功能实现方法【基于第三方扩展包】
Mar 11 PHP
PHP中->和=>的意思
Mar 31 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 preg_match_all结合str_replace替换内容中所有img
2008/10/11 PHP
《PHP编程最快明白》第三讲:php数组
2010/11/01 PHP
Avengerls vs KG BO3 第一场2.18
2021/03/10 DOTA
简单实用的js调试logger组件实现代码
2010/11/20 Javascript
jquery的map与get方法详解
2013/11/04 Javascript
原生javascript实现的分页插件pagenav
2014/08/28 Javascript
node.js中的fs.chown方法使用说明
2014/12/16 Javascript
jQuery背景插件backstretch使用指南
2015/04/21 Javascript
jquery实现适用于门户站的导航下拉菜单效果代码
2015/08/24 Javascript
JS实现可拖曳、可关闭的弹窗效果
2015/09/26 Javascript
jquery ezUI 双击行记录弹窗查看明细的实现方法
2016/06/01 Javascript
Vue.js每天必学之数据双向绑定
2016/09/05 Javascript
Vue表单验证插件的制作过程
2017/04/01 Javascript
jQuery实现分页功能(含ajax请求、后台数据、附完整demo)
2017/04/03 jQuery
基于JavaScript实现表格滚动分页
2017/11/22 Javascript
jQuery实现数字自动增加或者减少的动画效果示例
2018/12/11 jQuery
js JSON.stringify()基础详解
2019/06/19 Javascript
Vue路由前后端设计总结
2019/08/06 Javascript
layui动态表头的实现代码
2019/08/22 Javascript
浅谈JS中几种轻松处理'this'指向方式
2019/09/16 Javascript
原生js实现3D轮播图
2020/03/21 Javascript
微信小程序服务器日期格式化问题
2020/01/07 Javascript
微信小程序学习总结(二)样式、属性、模板操作分析
2020/06/04 Javascript
[40:19]完美世界DOTA2联赛PWL S3 Rebirth vs CPG 第二场 12.18
2020/12/19 DOTA
Python写的Tkinter程序屏幕居中方法
2015/03/10 Python
Python 读取某个目录下所有的文件实例
2018/06/23 Python
python实现给scatter设置颜色渐变条colorbar的方法
2018/12/13 Python
利用Python半自动化生成Nessus报告的方法
2019/03/19 Python
Python多线程threading模块用法实例分析
2019/05/22 Python
Django使用Channels实现WebSocket的方法
2019/07/28 Python
Tensorflow tf.nn.depthwise_conv2d如何实现深度卷积的
2020/04/20 Python
python实现图片,视频人脸识别(opencv版)
2020/11/18 Python
生物科学专业个人求职信范文
2013/12/07 职场文书
应聘教师求职信
2014/07/19 职场文书
《平行四边形的面积》教学反思
2016/02/16 职场文书
python-opencv 中值滤波{cv2.medianBlur(src, ksize)}的用法
2021/06/05 Python