天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

深入解析PHP中的(偽)多線程與多進程

(偽)多線程:借助外力
利用WEB服務器本身的多線程來處理,從WEB服務器多次調用我們需要實現多線程的程序。
QUOTE:
我們知道php本身是不支持多線程的, 但是我們的WEB服務器是支持多線程的.
也就是說可以同時讓多人一起訪問. 這也是我在php中實現多線程的基礎.
假設我們現在運行的是a.php這個文件. 但是我在程序中又請求WEB服務器運行另一個b.php
那么這兩個文件將是同時執(zhí)行的.
(PS: 一個鏈接請求發(fā)送之后, WEB服務器就會執(zhí)行它, 而不管客戶端是否已經退出)
有些時候, 我們想運行的不是另一個文件, 而是本文件中的一部分代碼.該怎么辦呢?
其實可是通過參數來控制a.php來運行哪一段程序.
下面看一個例子:
復制代碼 代碼如下:
<?php
function runThread(){
    $fp = fsockopen('localhost', 80, $errno, $errmsg);
    fputs($fp, "GET /a.php?act=brnrn");//這里的第二個參數是HTTP協議中規(guī)定的請求頭,不明白的請看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 比較一下兩個文件的中訪問的時間. 大家會發(fā)現, 這兩個的確是在不同線程中運行的.有些時間完全一樣.
上面只是一個簡單的例子, 大家可以改進成其它形式.
既然php中也能多線程了, 那么問題也來了, 那就是同步的問題. 我們知道 php本身是不支持多線程的. 所以更不會有什么像Java 中synchronize的方法了. 那我們該如何做呢.

1. 盡量不訪問同一個資源. 以避免沖突. 但是可以同時像數據庫操作. 因為數據庫是支持并發(fā)操作的. 所以在多線程的php
不要向同一個文件中寫入數據. 如果必須要寫的話, 用別的方法進行同步.. 如調用 flock對文件進行加鎖等. 或建立臨時文件并在另外的線程中等待這個文件的消失 while(file_exits('xxx')); 這樣就等于這個臨時文件存在時, 表示其實線程正在操作,如果沒有了這個文件, 說明其它線程已經釋放了這個.

2. 盡量不要從runThread在執(zhí)行fputs后取這個socket中讀取數據. 因為要實現多線程, 需要的用非阻塞模式. 即在像fgets這樣的函數時立即返回.. 所以讀寫數據就會出問題. 如果使用阻塞模式的話, 程序就不算是多線程了. 他要等上面的返回才執(zhí)行下面的程序. 所以如果需要交換數據最后利用外面文件或數據中完成. 實在想要的話就用socket_set_nonblock($fp) 來實現.

說了這么多, 倒底這個有沒有實際的意義呢? 在什么時候需要這種用這種方法呢 ?
答案是肯定的. 大家知道. 在一個不斷讀取網絡資源的應用中, 網絡的速度是瓶頸. 如果采多這種形式就可以同時以多個線程對不同的頁面進行讀取.

本人做的一個能從8848、soaso這些商城網站搜索信息的程序。還有一個從阿里巴巴網站上讀取商業(yè)信息和公司目錄的程序也用到了此技術。 因為這兩個程序都是要不斷的鏈接它們的服務器讀取信息并保存到數據庫。 利用此技術正好消除了在等待響應時的瓶頸。

多進程:使用php的Process Control Functions(PCNTL/線程控制函數)
只能用在Unix Like OS,Windows不可用。
編譯php的時候,需要加上--enable-pcntl,且推薦僅僅在CLI模式運行,不要在WEB服務器環(huán)境運行。
以下為簡短的測試代碼:
復制代碼 代碼如下:
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 toclipboard][qiao@oicq qiao]$ phptest.php       
Start
End
[qiao@oicq qiao]$ ps -aux | grep "php"
qiao     32275  0.0  0.5 49668 6148pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32276  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32277  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32278  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32279  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32280  0.0  0.5 49668 6152pts/1    S    14:03   0:00 /usr/local/php4/b
qiao     32281  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32282  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32283  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32284  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b
qiao     32286  0.0  0.0  1620  600pts/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 toclipboard][qiao@oicq qiao]$ phptest.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命令行腳本多進程并發(fā)執(zhí)行
除了fork, cli下的并發(fā)方式還有一種,看我的例子:
php不支持多線程,但是我們可以把問題轉換成“多進程”來解決。由于php中的pcntl_fork只有unix平臺才可以使用,所以本文嘗試使用popen來替代。
下面是一個例子:
被并行調用的子程序代碼:
復制代碼 代碼如下:
<?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);
    }
}
?>

主調用者程序,由他調用子進程,同時并發(fā)的收集子程序的輸出
復制代碼 代碼如下:
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
**總結:**
**主程序循環(huán)等待子進程, 通過fgets或fread 把子進程的輸出獲取出來 , 從時間戳上看,的確實現了并發(fā)執(zhí)行。**
-----------------------------------------------
以后的改進:
*  popen打開的句柄是單向的,如果需要向子進程交互,可以使用proc_open
*  使用數組和子函數代替while(!feof($handle1)|| !feof($handle2) || !feof($handle3) )這種齷齪的寫法
*  用fread一次把子進程已經產生的輸出取完,而不是每次一行。
一個并發(fā)執(zhí)行shell任務的調度者,本程序讀取一個任務文件,把里面的每行命令并發(fā)執(zhí)行, 可以設置同時存在的子進程數目:
復制代碼 代碼如下:
/*
   主任務管理器
   并發(fā)的執(zhí)行子任務列表
*/
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技術深入解析PHP中的(偽)多線程與多進程,轉載需保留來源!

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

主站蜘蛛池模板: 久久永久视频 | 真实处破女全过程完免费观看 | 翁公咬着小娇乳H边走边欢A | 国产精品麻豆a啊在线观看 国产精品麻豆AV | 成人国产在线不卡视频 | 亚洲国产在线精品国偷产拍 | 99re8在线视频精品 | 99RE久久精品国产 | 偷拍国产精品在线播放 | 综合人妻久久一区二区精品 | 国产亚洲精品久久无亚洲 | 秋秋影视午夜福利高清 | 亚洲精品tv久久久久久久久久 | 秋霞午夜鲁丝片午夜精品久 | 久久亚洲精品AV成人无 | 久久亚洲精品AV成人无码 | 人妻少妇69式99偷拍 | 精品国产免费第一区二区 | 小鸟酱喷水 | 久久国产免费一区二区三区 | 国产精品日本欧美一区二区 | 九九在线免费视频 | 年轻的的小婊孑2中文字幕 你是淫荡的我的女王 | 手机在线播放成人亚洲影院电影 | 午夜色情影院色a国产 | 7723日本高清完整版在线观看 | 91免费永久在线地址 | 中文字幕亚洲欧美日韩2o19 | 恋夜秀场1234手机视频在线观看 | 国产精品亚洲AV色欲在线观看 | 九九精彩视频在线观看视频 | 男女午夜性爽快免费视频不卡 | 亚洲国产在线2o20 | 亚洲视频无码高清在线 | 久久足恋网 | 偷拍精品视频一区二区三区 | 一本道本线中文无码 | 果冻传媒9CM在线观看 | zxfuli午夜福利在线 | 蜜芽亚洲欧美一区二区电影 | 国产又粗又黄又爽的大片 |