|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
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来连接数据库了,这期间你是怎么学会的,我们不去考证了,但是事实证明,你已经可以了。 |
|