仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1637|回复: 19
打印 上一主题 下一主题

[学习教程] PHP编程:PHP中的(伪)多线程与多历程

[复制链接]
海妖 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-2-4 00:13:29 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
理解动态语言的概念,运做机制,熟悉PHP语法多线程|历程    已由于没怎样需求,所以没有查这个的材料。比来有一个项目倒是需求如许子的功效。
    检查了PHP的手册和别人的例子,懂得到根基的两种办法:
(伪)多线程:借助外力
    使用WEB办事器自己的多线程来处置,从WEB办事器屡次挪用咱们需求完成多线程的法式。
    以下转载自:http://www.laikan8.com/21/118472.html



QUOTE:
咱们晓得PHP自己是不撑持多线程的, 然而咱们的WEB办事器是撑持多线程的.

也就是说可以同时让多人一同会见. 这也是我在PHP中完成多线程的基本.
假定咱们如今运转的是a.php这个文件. 然而我在法式中又恳求WEB办事器运转另外一个b.php
那末这两个文件将是同时履行的.
(PS: 一个链接恳求发送以后, WEB办事器就会履行它, 而不论客户端是不是已加入)
有些时分, 咱们想运转的不是另外一个文件, 而是本文件中的一局部代码.该怎样办呢?
其实可是经由过程参数来掌握a.php来运转哪一段法式.
上面看一个例子:
//a.php

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

CODE:[Copy to clipboard]<?php
    function runThread()
    {
        $fp = fsockopen('localhost', 80, $errno, $errmsg);
        fputs($fp, "GET /a.php?act=brnrn");        //这里的第二个参数是HTTP协定中划定的恳求头
                                //不分明的请看RFC中的界说
        fclose($fp);
    }
    function a()
    {
        $fp = fopen('result_a.log', 'w');
        fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");
        fclose($fp);        
    }
    function b()
    {
        $fp = fopen('result_b.log', 'w');
        fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");
        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的Process Control Functions(PCNTL/线程掌握函数)
    函数参考可见:http://www.php.net/manual/zh/ref.pcntl.php
    只能用在Unix Like OS,Windows不成用。
    编译php的时分,需求加上--enable-pcntl,且保举仅仅在CLI形式运转,不要在WEB办事器情况运转。
    以下为冗长的测试代码:
CODE:[Copy to clipboard]<?php
declare(ticks=1);
$bWaitFlag = FALSE; /// 是不是守候历程停止
$intNum = 10;           /// 历程总数
$pids = array();        ///  历程PID数组
echo ("Start\n");
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 ("End\n");
?>
运转了局以下:
CODE:[Copy to clipboard][qiao@oicq qiao]$ php test.php        
Start
End
[qiao@oicq qiao]$ ps -aux | grep "php"
qiao     32275  0.0  0.5 49668 6148 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32276  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32277  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32278  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32279  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32280  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32281  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32282  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32283  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32284  0.0  0.5 49668 6152 pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32286  0.0  0.0  1620  600 pts/1    S    14:03   0:00 grep php
[qiao@oicq qiao]$ 0 -> 1133503401  
1 -> 1133503402 *
2 -> 1133503403 **
3 -> 1133503404 ***
4 -> 1133503405 ****
5 -> 1133503406 *****
6 -> 1133503407 ******
7 -> 1133503408 *******
8 -> 1133503409 ********
9 -> 1133503410 *********
[qiao@oicq qiao]$
假如$bWaitFlag=TURE,则了局以下:
CODE:[Copy to clipboard][qiao@oicq qiao]$ php test.php        
Start
0 -> 1133503602  
wait 0 -> 1133503602
1 -> 1133503603 *
wait 1 -> 1133503603
2 -> 1133503604 **
wait 2 -> 1133503604
3 -> 1133503605 ***
wait 3 -> 1133503605
4 -> 1133503606 ****
wait 4 -> 1133503606
5 -> 1133503607 *****
wait 5 -> 1133503607
6 -> 1133503608 ******
wait 6 -> 1133503608
7 -> 1133503609 *******
wait 7 -> 1133503609
8 -> 1133503610 ********
wait 8 -> 1133503610
9 -> 1133503611 *********
wait 9 -> 1133503611
End
[qiao@oicq qiao]$
从多历程的例子可以看出,利用pcntl_fork()以后,将生成一个子历程,并且子历程运转的代码,从pcntl_fork()以后的代码入手下手,而子历程不承继父历程的数据信息(实践上是把父历程的数据做了一个全新的拷贝),因此利用if(!$pids[$i]) 来掌握子历程实践运转的代码段。
    更具体的研讨出于工夫关系,临时没有停止,你可以参考我给出的手册的链接。

[文章二] 测验考试php号令行剧本多历程并发履行
作者:dulao5
来历:http://dulao5.blog.hexun.com/3726837_d.html
除fork, cli下的并发体例还有一种,看我的例子:

php不撑持多线程,然而咱们可以把成绩转换成“多历程”来处理。因为php中的pcntl_fork只要unix平台才可使用,所以本文测验考试利用popen来替换。

上面是一个例子:
被并行挪用的子法式代码:
CODE:[Copy to clipboard]<?php
if($argc==1){
echo("argv\n");
}
$arg = $argv[1];
for($i=0; $i<10; $i++)
{
echo($i.".1.".time()." exec $arg \n");
if($arg=='php2')
{
sleep(1);
echo($i.".2.".time()." exec $arg \n");
sleep(1);
}
else
sleep(1);
}

?>
----------------------------
主挪用者法式,由他挪用子历程,同时并发的搜集子法式的输入
CODE:[Copy to clipboard]<?php
error_reporting(E_ALL);

$handle1 = popen('php sub.php php1', 'r');
$handle2 = popen('php sub.php php2', 'r');
$handle3 = popen('php sub.php php3', 'r');

echo "'$handle1'; " . gettype($handle1) . "\n";
echo "'$handle2'; " . gettype($handle2) . "\n";
echo "'$handle3'; " . gettype($handle3) . "\n";
//sleep(20);
while(!feof($handle1) || !feof($handle2) || !feof($handle3) )
{
$read = fgets($handle1);
echo $read;
$read = fgets($handle2);
echo $read;
$read = fgets($handle3);
echo $read;
}
pclose($handle1);
pclose($handle2);
pclose($handle3);
?>
-------------------
上面是我机械上的输入:
C:\my_hunter>php exec.php
'Resource id #4'; resource
'Resource id #5'; resource
'Resource id #6'; resource
0.1.1147935331 exec php1
0.1.1147935331 exec php2
0.1.1147935331 exec php3
1.1.1147935332 exec php1
0.2.1147935332 exec php2
1.1.1147935332 exec php3
2.1.1147935333 exec php1
1.1.1147935333 exec php2
2.1.1147935333 exec php3
3.1.1147935334 exec php1
1.2.1147935334 exec php2
3.1.1147935334 exec php3
4.1.1147935335 exec php1
2.1.1147935335 exec php2
4.1.1147935335 exec php3
5.1.1147935336 exec php1
2.2.1147935336 exec php2
5.1.1147935336 exec php3
6.1.1147935337 exec php1
3.1.1147935337 exec php2
6.1.1147935337 exec php3
7.1.1147935338 exec php1
3.2.1147935338 exec php2
7.1.1147935338 exec php3
8.1.1147935339 exec php1
4.1.1147935339 exec php2
8.1.1147935339 exec php3
9.1.1147935340 exec php1
4.2.1147935340 exec php2
9.1.1147935340 exec php3
5.1.1147935341 exec php2
5.2.1147935342 exec php2
6.1.1147935343 exec php2
6.2.1147935344 exec php2
7.1.1147935345 exec php2
7.2.1147935346 exec php2
8.1.1147935347 exec php2
8.2.1147935348 exec php2
9.1.1147935349 exec php2
9.2.1147935350 exec php2
**总结:**
**主法式轮回守候子历程, 经由过程fgets或fread 把子历程的输入获得出来 , 从工夫戳上看,切实其实完成了并发履行。**
-----------------------------------------------
今后的改善:
*  popen翻开的句柄是单向的,假如需求向子历程交互,可使用proc_open
*  利用数组和子函数取代while(!feof($handle1) || !feof($handle2) || !feof($handle3) )这类肮脏的写法
*  用fread一次把子历程已发生的输入取完,而不是每次一行。
一个并发履行shell义务的调剂者,本法式读取一个义务文件,把外面的每行号令并发履行, 可以设置同时存在的子历程数量:

CODE:[Copy to clipboard]<?
/*
   主义务办理器
   并发的履行子义务列表
*/
include("../common/conf.php");
include("../common/function.php");
//开启的历程数
$exec_number = 40 ;
/***** main ********/
if($argc==1){
    echo("argv\n");
}
$taskfile = $argv[1];
//tasklist
$tasklist = file($taskfile);
$tasklist_len = count($tasklist);
$tasklist_pos = 0;
$handle_list = array();
while(1)
{
    //子历程列表有余暇,则填充补齐子历程列表
    if($exec_number > count($handle_list) &&
            $tasklist_pos < $tasklist_len)
    {
        for($i=$tasklist_pos; $i<$tasklist_len; )
        {
            $command = $tasklist[$i] ;
            $handle_list[] = popen($command , "r" );
            tolog("begin task \t ".$tasklist[$i]);
            $i++;
            if($exec_number == count($handle_list)) break;
        }
        $tasklist_pos = $i;
    }
    //假如子历程列表空,加入
    if(0 == count($handle_list))
    {
        break;
    }
    //反省子历程列表的输入,把停失落的子历程封闭并纪录上去
    $end_handle_keys = array();
    foreach($handle_list as $key => $handle)
    {
        //$str = fgets($handle, 65536);
        $str = fread($handle, 65536);
        echo($str);
        if(feof($handle))
        {
            $end_handle_keys[] = $key;
            pclose($handle);
        }
    }
    //踢出停失落的子历程
    foreach($end_handle_keys as $key)
    {
        unset($handle_list[$key]);
        //var_dump($handle_list);
        //exit;
    }

}
tolog("\n\n*******************end**********************\n\n", "" ,  true);
?>

附加一段Socket多历程吸收的代码:
<?
do {
if (($msgsock = socket_accept($sock)) < 0) {
  echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
  break;
}
$pid = pcntl_fork();
if ($pid == -1) {
  die('could not fork');
} else if (!$pid) {
  .....
  socket_write($msgsock, $msg, strlen($msg));
  do {
   ......
  } while (true);
   socket_close($msgsock);
}
} while (true);
?>

终于理解了数据库的概念,而且让你兴奋不已的是你终于可以通过PHP来连接数据库了,这期间你是怎么学会的,我们不去考证了,但是事实证明,你已经可以了。
蒙在股里 该用户已被删除
沙发
发表于 2015-2-4 10:00:54 | 只看该作者
其实没啥难的,多练习,练习写程序,真正的实践比看100遍都有用。不过要熟悉引擎
海妖 该用户已被删除
板凳
 楼主| 发表于 2015-2-5 03:00:17 | 只看该作者
有位前辈曾经跟我说过,phper 至少要掌握200个函数 编起程序来才能顺畅点,那些不熟悉的函数记不住也要一拿手册就能找到。所以建议新手们没事就看看php的手册(至少array函数和string函数是要记牢的)。
因胸联盟 该用户已被删除
地板
发表于 2015-2-11 02:15:26 | 只看该作者
装在C盘下面可以利用windows的ghost功能可以还原回来(顺便当做是重转啦),当然啦我的编译目录要放在别的盘下,不然自己的劳动成果就悲剧啦。
小女巫 该用户已被删除
5#
发表于 2015-2-26 03:53:49 | 只看该作者
为了以后维护的方便最好是代码上都加上注释,“予人方便,自己方便”。此外开发文档什么的最好都弄齐全。我觉得这是程序员必备的素质。虽然会消耗点很多的时间。但是确实是非常有必要的。
飘飘悠悠 该用户已被删除
6#
发表于 2015-2-26 17:56:57 | 只看该作者
兴趣是最好的老师,百度是最好的词典。
活着的死人 该用户已被删除
7#
发表于 2015-3-1 21:45:14 | 只看该作者
不禁又想起那些说php是草根语言的人,为什么认得差距这么大呢。
若天明 该用户已被删除
8#
发表于 2015-3-7 04:51:38 | 只看该作者
如果你已经到这种程度了,那么你已经可以做我的老师了。其实php也分很多的区域,
admin 该用户已被删除
9#
发表于 2015-3-14 10:31:39 | 只看该作者
使用 jquery 等js框架的时候,要随时注意浏览器的更新情况,不然很容易发生框架不能使用。
灵魂腐蚀 该用户已被删除
10#
发表于 2015-3-21 01:47:16 | 只看该作者
我要在声明一下:我是个菜鸟!!我对php这门优秀的语言也是知之甚少。但是我要在这里说一下php在网站开发中最常用的几个功能:
愤怒的大鸟 该用户已被删除
11#
发表于 2015-3-24 08:21:08 | 只看该作者
最后介绍一个代码出错,但是老找不到错误方法,就是 go to wc (囧),出去换换气没准回来就找到错误啦。
小魔女 该用户已被删除
12#
发表于 2015-3-24 15:53:29 | 只看该作者
开发工具也会慢慢的更专业,每个公司的可能不一样,但是zend studio是个大伙都会用的。
小妖女 该用户已被删除
13#
发表于 2015-4-6 03:12:03 | 只看该作者
再就是混迹于论坛啦,咱们的phpchina的论坛就很强大,提出的问题一般都是有达人去解答的,以前的帖子也要多看看也能学到不少前辈们的经验。别的不错的论坛例如php100,javaeye也是很不错的。
若相依 该用户已被删除
14#
发表于 2015-4-12 22:22:00 | 只看该作者
Ps:以上纯属原创,如有雷同,纯属巧合
金色的骷髅 该用户已被删除
15#
发表于 2015-4-15 06:13:51 | 只看该作者
不禁又想起那些说php是草根语言的人,为什么认得差距这么大呢。
只想知道 该用户已被删除
16#
发表于 2015-4-17 21:51:39 | 只看该作者
首先我是坚决反对新手上来就用框架的,因为对底层的东西一点都不了解,造成知识上的真空,会对以后的发展不利。我的观点上手了解下框架就好,代码还是手写。当然啦如果是位别的编程语言的高手的话,这个就另当别论啦。
飘灵儿 该用户已被删除
17#
发表于 2015-4-19 07:53:38 | 只看该作者
实践是检验自己会不会的真理。
再现理想 该用户已被删除
18#
发表于 2015-4-22 18:11:14 | 只看该作者
使用 jquery 等js框架的时候,要随时注意浏览器的更新情况,不然很容易发生框架不能使用。
再见西城 该用户已被删除
19#
发表于 2015-4-25 23:37:50 | 只看该作者
,熟悉html,能用div+css,还有javascript,优先考虑linux。我在开始学习的时候,就想把这些知识一起学习,我天真的认为同时学习能够互相呼应,因为知识是相通的。
爱飞 该用户已被删除
20#
发表于 2015-4-26 10:12:10 | 只看该作者
这些都是最基本最常用功能,我们这些菜鸟在系统学习后,可以先对这些功能深入研究。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-5-13 01:19

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表