PHP实现多线程的两个方法 PHP+shell

 更新时间:2016年11月25日 17:36  点击:2032
PHP本身是不是支持多线程的,不过我们可以借助其他的方法来实现多线程,比如 shell 服务,比如 web 服务器,本文我们来讲讲这两个方法如何实现。

PHP+shell实现多线程的方法

先写个简单的php代码,这里为了让脚本执行时间更长,方便看效果,sleep一下,呵呵!先看下test.php的代码:ls

PHP代码:

<?php
for ($i=0;$i<10;$i++) {
  echo $i;
  sleep(10);
}
?>

在看下shell脚本的代码,非常简单

#!/bin/bash
for i in 1 2 3 4 5 6 7 8 9 10
do
  /usr/bin/php -q /var/www/html/test.php &
done

注意到在请求php代码的那行有一个&符号吗,这个是关键,不加的话是不能进行多线程的,&表示讲服务推送到后台执行,因此,在 shell的每次的循环中不必等php的代码全部执行完在请求下一个文件,而是同时进行的,这样就实现了多线程,下面运行下shell看下效果,这里你将 看到10个test.php进程再跑,再利用linux的定时器,定时请求这个shell,在处理一些需要多线程的任务,例如,批量下载时,非常好用!



php中用WEB服务器实现多线程

假设我们现在运行的是a.php这个文件. 但是我在程序中又请求WEB服务器运行另一个b.php,那么这两个文件将是同时执行的.(PS: 一个链接请求发送之后, WEB服务器就会执行它, 而不管客户端是否已经退出)

有些时候, 我们想运行的不是另一个文件, 而是本文件中的一部分代码.该怎么办呢?
其实可是通过参数来控制a.php来运行哪一段程序.

下面看一个例子:

//a.php,b.php

PHP代码:--------------------------------------------------------------------------------

<?php
    function runThread()
    {
        $fp = fsockopen('localhost', 80, $errno, $errmsg);
         
        fputs($fp, "GET /b.php?act=b\r\n\r\n");        //这里的第二个参数是HTTP协议中规定的请求头
                                //不明白的请看RFC中的定义
         
        fclose($fp);
    }
 
    function a()
    {
        $fp = fopen('result_a.log', 'w');
        fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "\r\n");
         
        fclose($fp);         
    }
 
    function b()
    {
        $fp = fopen('result_b.log', 'w');
        fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "\r\n");
         
        fclose($fp);         
    }
 
    if(!isset($_GET['act'])) $_GET['act'] = 'a';
     
    if($_GET['act'] == 'a')
    {
        runThread();
        a();
    }
    else if($_GET['act'] == 'b') b();
?>
--------------------------------------------------------------------------------


打开result_a.log 和 result_b.log 比较一下两个文件的中访问的时间. 大家会发现, 这两个的确是在不同线程中运行的.有些时间完全一样.

上面只是一个简单的例子, 大家可以改进成其它形式.


既然PHP中也能多线程了, 那么问题也来了, 那就是同步的问题. 我们知道 PHP本身是不支持多线程的. 所以更不会有什么像Java 中synchronize的方法了. 那我们该如何做呢.

1. 尽量不访问同一个资源. 以避免冲突. 但是可以同时像数据库操作. 因为数据库是支持并发操作的. 所以在多线程的PHP中不要向同一个文件中写入数据. 如果必须要写的话, 用别的方法进行同步.. 如调用 flock对文件进行加锁等. 或建立临时文件并在另外的线程中等待这个文件的消失 while(file_exits('xxx')); 这样就等于这个临时文件存在时, 表示其实线程正在操作

如果没有了这个文件, 说明其它线程已经释放了这个.

2. 尽量不要从runThread在执行fputs后取这个socket中读取数据. 因为要实现多线程, 需要的用非阻塞模式. 即在像fgets这样的函数时立即返回.. 所以读写数据就会出问题. 如果使用阻塞模式的话, 程序就不算是多线程了. 他要等上面的返回才执行下面的程序. 所以如果需要交换数据最后利用外面文件或数据中完成. 实在想要的话就用socket_set_nonblock($fp) 来实现.


说了这么多, 倒底这个有没有实际的意义呢? 在什么时候需要这种用这种方法呢 ?
答案是肯定的. 大家知道. 在一个不断读取网络资源的应用中, 网络的速度是瓶颈. 如果采多这种形式就可以同时以多个线程对不同的页面进行读取.

本人做的一个能从8848、soaso这些商城网站搜索信息的程序。还有一个从阿里巴巴网站上读取商业信息和公司目录的程序也用到了此技术。 因为这两个程序都是要不断的链接它们的服务器读取信息并保存到数据库。 利用此技术正好消除了在等待响应时的瓶颈。




php模拟实现多线程的三种方法

PHP语言本身是不支持多线程的. 总结了一下网上关于PHP模拟多线程的方法, 总的来说, 都是利用了PHP的好伙伴们本身所具有的多线程能力. PHP的好伙伴指的就是LINUX和APACHE啦, LAMP嘛.

  另外, 既然是模拟的, 就不是真正的多线程. 其实只是多进程. 进程和线程是两个不同的概念. 好了, 以下方法都是从网上找来的.

  1. 利用LINUX操作系统

<?php
for ($i=0;$i<10;$i++) {
  echo $i;
  sleep(5);
}
?>

上面存成test.php, 然后写一段SHELL代码

#!/bin/bash
for i in 1 2 3 4 5 6 7 8 9 10
do
  php -q test.php &
done

2. 利用fork子进程(其实同样是利用LINUX操作系统)

<?php
declare(ticks=1);
$bWaitFlag = FALSE; /// 是否等待进程结束
$intNum = 10;      /// 进程总数
$pids = array();    /// 进程PID数组
echo ("Startn");
for($i = 0; $i < $intNum; $i++) {
 $pids[$i] = pcntl_fork();/// 产生子进程,而且从当前行之下开试运行代码,而且不继承父进程的数据信息
 if(!$pids[$i]) {
  // 子进程进程代码段_Start
  $str="";
  sleep(5+$i);
  for ($j=0;$j<$i;$j++) {$str.="*";}
  echo "$i -> " . time() . " $str n";
  exit();
  // 子进程进程代码段_End
 }
}
if ($bWaitFlag)
{
 for($i = 0; $i < $intNum; $i++) {
  pcntl_waitpid($pids[$i], $status, WUNTRACED);
  echo "wait $i -> " . time() . "n";
 }
}
echo ("Endn");
?>

3. 利用WEB SERVER, PHP不支持多线程, APACHE可是支持的, 呵呵.

假设我们现在运行的是a.php这个文档. 但是我在程式中又请求WEB服务器运行另一个b.php

那么这两个文档将是同时执行的.(代码同上)

当然啦,也可以把需要多线程处理的部分交给JAVA去处理, 然后在PHP里调用, 哈哈.

<?php
system('java multiThread.java');
?>

cURL在php中用到的比较多了我们通常把它用于采集访问或数据模拟提交上了,但在用到https时我们使用常用的办法会提示报错:Unknown SSL protocol error in connection to了,对于这个问题我们一起来看解决办法。


因爆出“OpenSSL Heartbleed”与“SSLv3中间人攻击”等漏洞,很多平台关闭掉SSLv2、SSLv3版本支持,不再支持部分使用SSLv2、 SSLv3或更低版本的客户端调用如微信公众平台等。
如果cURL操作https的url,请附带如下选项即可:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, 1);


例子

PHP CURL HTTPS POST
function vpost($url,$data){ // 模拟提交数据函数
    $curl = curl_init(); // 启动一个CURL会话
    curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中检查SSL加密算法是否存在
    curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
    curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
    curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
    curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
    curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
    $tmpInfo = curl_exec($curl); // 执行操作
    if (curl_errno($curl)) {
       echo 'Errno'.curl_error($curl);//捕抓异常
    }
    curl_close($curl); // 关闭CURL会话
    return $tmpInfo; // 返回数据
}


$url = "https://xxx.xxx.xxx/xxx";
$data ="x=xxxxxx";
$result = vpost($url,$data);

无法上传大文件是因为php.ini配置有限制了,这样限制了用户默认最大为2MB了,超过了就不能上传了,如果你确实要上传我们可以按下面方法来处理一下。


打开php.ini,

参数  设置  说明 

file_uploads  on  是否允许通过HTTP上传文件的开关。默认为ON即是开 
upload_tmp_dir  –  文件上传至服务器上存储临时文件的地方,如果没指定就会用系统默认的临时文件夹 
upload_max_filesize  8m  望文生意,即允许上传文件大小的最大值。默认为2M 
post_max_size  8m  指通过表单POST给PHP的所能接收的最大值,包括表单里的所有值。默认为8M 
说明 
一般地,设置好上述四个参数后,在网络正常的情况下,上传<=8M的文件是不成问题 
但如果要上传>8M的大体积文件,只设置上述四项还一定能行的通。除非你的网络真有100M/S的上传高速,否则你还得继续设置下面的参数。 

max_execution_time  600  每个PHP页面运行的最大时间值(秒),默认30秒 
max_input_time  600  每个PHP页面接收数据所需的最大时间,默认60秒 
memory_limit  8m  每个PHP页面所吃掉的最大内存,默认8M 

把上述参数修改后,在网络所允许的正常情况下,就可以上传大体积文件了

[编辑]论坛文件上传常见错误类型(不断总结中…)
Warning: Unable to open ‘\\php2′ for reading: Invalid argument in e:\user\web\larksoft.net\upload\upfile.php on line 10
是php的upload_tmp_dir的原因,所指定的目录必须可读可写

Parse error: parse error in c:\program files\apache group\apache\htdocs\mdweb\ftpfile\upload.php on line 14
Parse error一般都是语句的问题,比如象“;”,“’”,“)”等等的匹配问题

搞定了,是/etc/httpd/conf.d/php.conf里面,有一个
<Files *.php>
SetOutputFilter PHP
SetInputFilter PHP
LimitRequestBody 524288
</Files>

把LimitRequestBody 改了就可以了。


如果你是Nginx的话还需要修改nginx.conf中的http段:

1. 修改PHP配置文件中的三项:vim /usr/local/php/etc/php.ini
(1)post_max_size = 50M      #PHP可接受的最大POST数据
(2)upload_max_filesize = 50M   #文件上传允许的最大值
(3)max_execution_time = 300   #每个脚本的最大执行时间,秒钟(0则不限制,不建议设0)
2. 修改Nginx配置文件:vim /usr/local/nginx/conf/nginx.conf (如果忘了配置文件的具体位置,可以使用 locate nginx.conf 查找)
(1)client_max_body_size 50m   #客户端最大上传大小 50M
3. 重启PHP:/etc/init.d/php-fpm restart
4. 平滑重启Nginx:/usr/local/nginx/sbin/nginx -s reload

file_get_contents函数在获得远程文件时提示Warning: file_get_contents failed to open stream,希望例子能够帮助到各位,希望例子能够帮助到大家。

在做项目时用 file_get_contents 来获取数据,php 报错  PHP Warning: file_get_contents failed to open stream: no suitable wrapper could be found.

最后用了curl来获取数据!

今天百度了一下!找到了解决办法!先拷贝过来!

系统:centos 6.5

在错误日志中,php报的错误是

Warning: file_get_contents() [function.file-get-contents]: http:// wrapper is disabled in the server configuration by allow_url_fopen=0

failed to open stream: no suitable wrapper could be found

此警告说服务器阻止访问远程文件,修改php.ini

allow_url_fopen = Off
allow_url_include = Off

改成

allow_url_fopen = On
allow_url_include = On

重启php-fpm或者重启web服务。

如果还是无法解决 的话

windows下处理方法:

c:\windows\php.ini

extension=php_openssl.dll 把前的;去掉,重启iis服务。

linux下处理方法:

/etc/php.ini

extension=php_openssl.dll 把前的;去掉,重启apache服务。


如果上面问题没能解决我的问题,我们可以如下测试。


$context = stream_context_create(array('http'=>array('ignore_errors'=>true)));
$contents = file_get_contents($url, FALSE, $context);

可以请求时,忽略错误。可以解决警告信息

php5.3和5.4、5.5不能加载php_curl.dll问题不光在win7中就连在win2003与xp系统都会有碰到过了,对于这个问题我们来看解决办法。


最近分别在WIN7和Windows8 上分别安装php 高版本!都遇到了这个问题!

一.win7系统32位, apache2.2, php5.2升级到5.4.

这个比较容易:
1. phpinfo发现没有加载curl;
2. 在php.ini中设置extension_dir 指向e:\php5.4\ext; 部分php扩展加载了, 但curl仍不行。
3. 设置windows系统环境变量, phpext, 指向e:\php5.4\ext, PHPRC 指向e:\php5.4
设置path环境变量,添加e:\php5.4
4. 重新启动apache, 终于出来了, 大功告成!

一.win8系统64位, apache2.2,   php5.4 , v9版

首先前面win7下的都执行了, 但死活不行!也下载了许多64位的php_curl.dll, 也不行!

发现很可能是加载依赖的问题! 最后解决!

1. 执行前面win7下的1-4步!

2. 下载:http://www.dependencywalker.com/

3. 查看php版本中的php_curl.dll模块依赖!自行尝试吧, 运行并找到php_curl.dll路径, 打开查看依赖库。
4. 在http.conf中最后,添加:

LoadFile E:/PHP/php-5.4/php5ts.dll
LoadFile E:/PHP/php-5.4/libeay32.dll
LoadFile E:/PHP/php-5.4/ssleay32.dll
LoadFile E:/PHP/php-5.4/libssh2.dll


LoadModule php5_module "E:/PHP/php-5.4/php5apache2_2.dll"
AddType application/x-httpd-php .php
5. 重启, 大功靠成!
三总结:
主要问题, php要下载安全版本;各种方法都试了, 仍不行, 就查看依赖并尝试添加。
php包里的php_curl.dll扩展应是可用的, 不要随便替换。 另外, 尽管php文档注释32位环境才需加依赖, 事实不是这样。
花了我半天的时间!

[!--infotagslink--]

相关文章

  • C# WinForm多线程解决界面卡死问题的完美解决方案,使用BeginInvoke

    问题描述:当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决一个主线程来创建界...2020-06-24
  • php 中file_get_contents超时问题的解决方法

    file_get_contents超时我知道最多的原因就是你机器访问远程机器过慢,导致php脚本超时了,但也有其它很多原因,下面我来总结file_get_contents超时问题的解决方法总结。...2016-11-25
  • HTTP 408错误是什么 HTTP 408错误解决方法

    相信很多站长都遇到过这样一个问题,访问页面时出现408错误,下面一聚教程网将为大家介绍408错误出现的原因以及408错误的解决办法。 HTTP 408错误出现原因: HTT...2017-01-22
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • php抓取网站图片并保存的实现方法

    php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
  • ps把文字背景变透明的操作方法

    ps软件是现在非常受大家喜欢的一款软件,有着非常不错的使用功能。这次文章就给大家介绍下ps把文字背景变透明的操作方法,喜欢的一起来看看。 1、使用Photoshop软件...2017-07-06
  • intellij idea快速查看当前类中的所有方法(推荐)

    这篇文章主要介绍了intellij idea快速查看当前类中的所有方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-02
  • Mysql select语句设置默认值的方法

    1.在没有设置默认值的情况下: 复制代码 代码如下:SELECT userinfo.id, user_name, role, adm_regionid, region_name , create_timeFROM userinfoLEFT JOIN region ON userinfo.adm_regionid = region.id 结果:...2014-05-31
  • js导出table数据到excel即导出为EXCEL文档的方法

    复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta ht...2013-10-13
  • js基础知识(公有方法、私有方法、特权方法)

    本文涉及的主题虽然很基础,在许多人看来属于小伎俩,但在JavaScript基础知识中属于一个综合性的话题。这里会涉及到对象属性的封装、原型、构造函数、闭包以及立即执行表达式等知识。公有方法 公有方法就是能被外部访问...2015-11-08
  • mysql 批量更新与批量更新多条记录的不同值实现方法

    批量更新mysql更新语句很简单,更新一条数据的某个字段,一般这样写:复制代码 代码如下:UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_value';如果更新同一字段为同一个值,mysql也很简单,修改下where即...2013-10-04
  • c#中分割字符串的几种方法

    单个字符分割 string s="abcdeabcdeabcde"; string[] sArray=s.Split('c'); foreach(string i in sArray) Console.WriteLine(i.ToString()); 输出下面的结果: ab de...2020-06-25
  • ps怎么制作倒影 ps设计倒影的方法

    ps软件是一款非常不错的图片处理软件,有着非常不错的使用效果。这次文章要给大家介绍的是ps怎么制作倒影,一起来看看设计倒影的方法。 用ps怎么做倒影最终效果&#819...2017-07-06
  • PHP 验证码不显示只有一个小红叉的解决方法

    最近想自学PHP ,做了个验证码,但不知道怎么搞的,总出现一个如下图的小红叉,但验证码就是显示不出来,原因如下 未修改之前,出现如下错误; (1)修改步骤如下,原因如下,原因是apache权限没开, (2)点击打开php.int., 搜索extension=ph...2013-10-04
  • 安卓手机wifi打不开修复教程,安卓手机wifi打不开解决方法

    手机wifi打不开?让小编来告诉你如何解决。还不知道的朋友快来看看。 手机wifi是现在生活中最常用的手机功能,但是遇到手机wifi打不开的情况该怎么办呢?如果手机wifi...2016-12-21
  • 连接MySql速度慢的解决方法(skip-name-resolve)

    最近在Linux服务器上安装MySql5后,本地使用客户端连MySql速度超慢,本地程序连接也超慢。 解决方法:在配置文件my.cnf的[mysqld]下加入skip-name-resolve。原因是默认安装的MySql开启了DNS的反向解析。如果禁用的话就不能...2015-10-21
  • js控制页面控件隐藏显示的两种方法介绍

    javascript控制页面控件隐藏显示的两种方法,方法的不同之处在于控件隐藏后是否还在页面上占位 方法一: 复制代码 代码如下: document.all["panelsms"].style.visibility="hidden"; document.all["panelsms"].style.visi...2013-10-13
  • C#方法的总结详解

    本篇文章是对C#方法进行了详细的总结与介绍,需要的朋友参考下...2020-06-25
  • c# 多线程处理多个数据的方法

    这篇文章主要介绍了c# 多线程处理多个数据的方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...2021-03-31
  • Zend studio文件注释模板设置方法

    步骤:Window -> PHP -> Editor -> Templates,这里可以设置(增、删、改、导入等)管理你的模板。新建文件注释、函数注释、代码块等模板的实例新建模板,分别输入Name、Description、Patterna)文件注释Name: 3cfileDescriptio...2013-10-04