PHP编程:PHP中的(伪)多线程与多历程
理解动态语言的概念,运做机制,熟悉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:<?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:<?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:$ php test.php
Start
End
$ ps -aux | grep "php"
qiao 322750.00.5 49668 6148 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322760.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322770.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322780.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322790.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322800.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322810.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322820.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322830.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322840.00.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
qiao 322860.00.01620600 pts/1 S 14:03 0:00 grep php
$ 0 -> 1133503401
1 -> 1133503402 *
2 -> 1133503403 **
3 -> 1133503404 ***
4 -> 1133503405 ****
5 -> 1133503406 *****
6 -> 1133503407 ******
7 -> 1133503408 *******
8 -> 1133503409 ********
9 -> 1133503410 *********
$
假如$bWaitFlag=TURE,则了局以下:
CODE:$ 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
$
从多历程的例子可以看出,利用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:<?php
if($argc==1){
echo("argv\n");
}
$arg = $argv;
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:<?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:<?
/*
主义务办理器
并发的履行子义务列表
*/
include("../common/conf.php");
include("../common/function.php");
//开启的历程数
$exec_number = 40 ;
/***** main ********/
if($argc==1){
echo("argv\n");
}
$taskfile = $argv;
//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来连接数据库了,这期间你是怎么学会的,我们不去考证了,但是事实证明,你已经可以了。 其实没啥难的,多练习,练习写程序,真正的实践比看100遍都有用。不过要熟悉引擎 有位前辈曾经跟我说过,phper 至少要掌握200个函数 编起程序来才能顺畅点,那些不熟悉的函数记不住也要一拿手册就能找到。所以建议新手们没事就看看php的手册(至少array函数和string函数是要记牢的)。 装在C盘下面可以利用windows的ghost功能可以还原回来(顺便当做是重转啦),当然啦我的编译目录要放在别的盘下,不然自己的劳动成果就悲剧啦。 为了以后维护的方便最好是代码上都加上注释,“予人方便,自己方便”。此外开发文档什么的最好都弄齐全。我觉得这是程序员必备的素质。虽然会消耗点很多的时间。但是确实是非常有必要的。 兴趣是最好的老师,百度是最好的词典。 不禁又想起那些说php是草根语言的人,为什么认得差距这么大呢。 如果你已经到这种程度了,那么你已经可以做我的老师了。其实php也分很多的区域, 使用 jquery 等js框架的时候,要随时注意浏览器的更新情况,不然很容易发生框架不能使用。 我要在声明一下:我是个菜鸟!!我对php这门优秀的语言也是知之甚少。但是我要在这里说一下php在网站开发中最常用的几个功能: 最后介绍一个代码出错,但是老找不到错误方法,就是 go to wc (囧),出去换换气没准回来就找到错误啦。 开发工具也会慢慢的更专业,每个公司的可能不一样,但是zend studio是个大伙都会用的。 再就是混迹于论坛啦,咱们的phpchina的论坛就很强大,提出的问题一般都是有达人去解答的,以前的帖子也要多看看也能学到不少前辈们的经验。别的不错的论坛例如php100,javaeye也是很不错的。 Ps:以上纯属原创,如有雷同,纯属巧合 不禁又想起那些说php是草根语言的人,为什么认得差距这么大呢。 首先我是坚决反对新手上来就用框架的,因为对底层的东西一点都不了解,造成知识上的真空,会对以后的发展不利。我的观点上手了解下框架就好,代码还是手写。当然啦如果是位别的编程语言的高手的话,这个就另当别论啦。 实践是检验自己会不会的真理。 使用 jquery 等js框架的时候,要随时注意浏览器的更新情况,不然很容易发生框架不能使用。 ,熟悉html,能用div+css,还有javascript,优先考虑linux。我在开始学习的时候,就想把这些知识一起学习,我天真的认为同时学习能够互相呼应,因为知识是相通的。 这些都是最基本最常用功能,我们这些菜鸟在系统学习后,可以先对这些功能深入研究。
页:
[1]