>백엔드 개발 >PHP 튜토리얼 >PHP의 다중 프로세스 프로그래밍 방법에 대한 심층 탐구_php 기술

PHP의 다중 프로세스 프로그래밍 방법에 대한 심층 탐구_php 기술

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB원래의
2016-05-16 20:08:271127검색

하위 프로세스 생성
하위 프로세스를 작성하는 일반적인 방법은 다음과 같습니다.

<&#63;php
$pid = pcntl_fork();
if($pid == -1){
     //创建失败
     die('could not fork');
}
else{
    if($pid){
        //从这里开始写的代码是父进程的
        exit("parent!");
    }
    else{
        //子进程代码,为防止不停的启用子进程造成系统资源被耗尽的情况,一般子进程代码运行完成后,加入exit来确保子进程正常退出。
        exit("child");
    }
}
&#63;>

위 코드가 성공적으로 하위 프로세스를 생성하면 시스템에는 두 개의 프로세스가 있게 됩니다. 하나는 상위 프로세스이고 다른 하나는 하위 프로세스의 ID 번호입니다. 시스템이 $pid = pcntl_fork();에 도달하면 이 위치에서 분기가 만들어지고 상위 프로세스와 하위 프로세스가 각각 자신의 프로그램 코드를 실행하기 시작합니다. 코드의 실행 결과는 부모와 자식입니다. 이상합니다. if와 else가 상호 배타적인 코드에 결과가 출력되는 이유는 무엇입니까? 실제로 위에서 언급한 것처럼 pcntl_fork에 코드가 있으면 부모 프로세스는 parent를 실행하고 자식 프로세스는 child를 실행합니다. 상위 및 하위가 코드 결과에 표시됩니다. 누가 먼저인지는 시스템 리소스 할당에 따라 달라집니다.

데이터 처리를 위해 여러 프로세스를 시작해야 하는 경우 합의된 수당 1,000개 프로세스 등 데이터 양에 따라 하위 프로세스를 시작할 수 있습니다. for 루프를 사용하면 됩니다. 

 #如果获得的总数小于或等于0,等待60秒,并退出
  if ($count <= 0) 
  {
    sleep(60);
    exit;
  }
  #如果大于1000,计算需要起的进程数
  if ($count > 1000)
  {
    $cycleSize = ceil($count/1000);
  }
  else
  {
    $cycleSize = 1;
  }
  
  for ($i=0; $i<$cycleSize; $i++)
  {
    $pid  = pcntl_fork();
    if($pid == -1)
    {
      break;
    }
    else
    {
      if($pid)
      {
        #父进程获得子进程的pid,存入数组
        $pidArr[] = $pid;
      }
      else
      {
        //开始发送,子进程执行完自己的任务后,退出。
          exit;
      }
    }
  }
  
  while(count($pidArr) > 0)
  {
    $myId  = pcntl_waitpid(-1, $status, WNOHANG);
    foreach($pidArr as $key => $pid)
    {
      if($myId == $pid) unset($pidArr[$key]);
    }
  }

그런 다음 crontab을 사용하여 이 PHP 프로그램이 일정한 간격으로 자동 실행되도록 하세요.

물론 샘플 코드는 비교적 간단합니다. 특히 여러 하위 프로세스가 동일한 데이터를 실행하지 못하게 하거나 현재 프로세스가 데이터 처리를 완료하지 않은 경우 crontab이 PHP 실행을 시작하는 방법을 고려해야 합니다. 새로운 프로세스 등을 활성화하려면 파일을 다시 작성하세요.


PHP 다중 프로세스 구현 방법
PHP 멀티 프로세스 구현을 체계적으로 정리해보자:

1.직접방식

pcntl_fork()는 프로세스를 생성합니다. 상위 프로세스의 반환 값은 하위 프로세스의 pid입니다. -1은 프로세스 생성이 실패했음을 나타냅니다. C와 매우 유사합니다.

테스트 스크립트 test.php

<&#63;php
  // example of multiple processes
  date_default_timezone_set( 'Asia/Chongqing');
  echo "parent start, pid ", getmypid(), "\n" ;
  beep();
  for ($i=0; $i<3; ++$i){
     $pid = pcntl_fork();
      if ($pid == -1){
         die ("cannot fork" );
     } else if ($pid > 0){
         echo "parent continue \n";
         for ($k=0; $k<2; ++$k){
           beep();
        }
     } else if ($pid == 0){
         echo "child start, pid ", getmypid(), "\n" ;
         for ($j=0; $j<5; ++$j){
           beep();
        }
         exit ;
     }
  }
  // ***
  function beep(){
      echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;
     sleep(1);
  }
&#63;>

명령줄에서 실행

#php -f test.php

출력결과

parent start, pid 1793
1793  2013-01-14 15:04:17
parent continue
1793  2013-01-14 15:04:18
child start, pid 1794
1794  2013-01-14 15:04:18
1794  2013-01-14 15:04:19
1793  2013-01-14 15:04:19
1794  2013-01-14 15:04:20
parent continue
1793  2013-01-14 15:04:20
child start, pid 1795
1795  2013-01-14 15:04:20
17931794        2013-01-14 15:04:212013-01-14 15:04:21

1795  2013-01-14 15:04:21
1794  2013-01-14 15:04:22
1795  2013-01-14 15:04:22
parent continue
1793  2013-01-14 15:04:22
child start, pid 1796
1796  2013-01-14 15:04:22
1793  2013-01-14 15:04:23
1796  2013-01-14 15:04:23
1795  2013-01-14 15:04:23
1795  2013-01-14 15:04:24
1796  2013-01-14 15:04:24
1796  2013-01-14 15:04:25
1796  2013-01-14 15:04:26

보시다시피 3개의 하위 프로세스가 생성되어 상위 프로세스와 병렬로 실행됩니다. 한 줄의 형식이 다른 줄과 약간 다릅니다.
17931794 2013-01-14 15:04:212013-01-14 15:04:21
두 프로세스가 동시에 쓰기 작업을 수행하기 때문에 충돌이 발생합니다.


2. 차단방법

직접 방법을 사용하면 상위 프로세스가 하위 프로세스를 생성한 후 하위 프로세스가 끝날 때까지 기다리지 않고 계속 실행됩니다. 여기에는 아무런 문제가 없는 것 같습니다. PHP 스크립트가 실행 후 자동으로 종료되지 않고 메모리에 상주하는 경우 하위 프로세스를 재활용할 수 없는 문제가 발생합니다. 즉, 좀비 프로세스입니다. pcntl_wai() 메소드를 통해 프로세스가 종료될 때까지 기다린 후 종료된 프로세스를 재활용할 수 있습니다.
테스트 스크립트를 다음으로 변경합니다.

$pid = pcntl_fork();
if ($pid == -1){
  ...
} else if ($pid > 0){
   echo "parent continue \n";
   pcntl_wait($status);
   for ($k=0; $k<2; ++$k){
     beep();
  }
} else if ($pid == 0){
   ...
}

명령줄에서 실행

#php -f test.php

출력결과

parent start, pid 1807
1807  2013-01-14 15:20:05
parent continue
child start, pid 1808
1808  2013-01-14 15:20:06
1808  2013-01-14 15:20:07
1808  2013-01-14 15:20:08
1808  2013-01-14 15:20:09
1808  2013-01-14 15:20:10
1807  2013-01-14 15:20:11
1807  2013-01-14 15:20:12
parent continue
child start, pid 1809
1809  2013-01-14 15:20:13
1809  2013-01-14 15:20:14
1809  2013-01-14 15:20:15
1809  2013-01-14 15:20:16
1809  2013-01-14 15:20:17
1807  2013-01-14 15:20:18
1807  2013-01-14 15:20:19
child start, pid 1810
1810  2013-01-14 15:20:20
parent continue
1810  2013-01-14 15:20:21
1810  2013-01-14 15:20:22
1810  2013-01-14 15:20:23
1810  2013-01-14 15:20:24
1807  2013-01-14 15:20:25
1807  2013-01-14 15:20:26

상위 프로세스는 pcntl_wait()에서 자신을 차단하고 계속하기 전에 하위 프로세스의 실행이 완료될 때까지 기다립니다.


3. 논블로킹 방식

차단 방식은 여러 프로세스의 병렬성을 잃습니다. 완성된 하위 프로세스를 재활용하고 병렬화하는 방법도 있습니다. 이것이 논블로킹(Non-Blocking) 방식입니다.
스크립트 수정:

<&#63;php
  // example of multiple processes
  date_default_timezone_set( 'Asia/Chongqing');
  declare (ticks = 1);
  pcntl_signal(SIGCHLD, "garbage" );
  echo "parent start, pid ", getmypid(), "\n" ;
  beep();
  for ($i=0; $i<3; ++$i){
     $pid = pcntl_fork();
      if ($pid == -1){
         die ("cannot fork" );
     } else if ($pid > 0){
         echo "parent continue \n";
         for ($k=0; $k<2; ++$k){
           beep();
        }
     } else if ($pid == 0){
         echo "child start, pid ", getmypid(), "\n" ;
         for ($j=0; $j<5; ++$j){
           beep();
        }
         exit (0);
     }
  }
  // parent
  while (1){
      // do something else
     sleep(5);
  }
  // ***
  function garbage($signal){
      echo "signel $signal received\n" ;
      
      while (($pid = pcntl_waitpid(-1, $status, WNOHANG))> 0){
         echo "\t child end pid $pid , status $status\n" ;
     }
  }
  function beep(){
      echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;
     sleep(1);
  }
&#63;>

명령줄에서 실행

#php -f test.php &

출력결과

parent start, pid 2066
2066  2013-01-14 16:45:34
parent continue
2066  2013-01-14 16:45:35
child start, pid 2067
2067  2013-01-14 16:45:35
20662067        2013-01-14 16:45:362013-01-14 16:45:36

2067  2013-01-14 16:45:37
parent continue
2066  2013-01-14 16:45:37
child start, pid 2068
2068  2013-01-14 16:45:37
2067  2013-01-14 16:45:38
2068  2013-01-14 16:45:38
2066  2013-01-14 16:45:38
parent continue
2066  2013-01-14 16:45:40
child start, pid 2069
2069  2067  2013-01-14 16:45:40
2013-01-14 16:45:40
2068  2013-01-14 16:45:40
2066  2013-01-14 16:45:41
2069  2013-01-14 16:45:41
2068  2013-01-14 16:45:41
signel 17 received
     child end pid 2067, status 0
2069  2013-01-14 16:45:42
2068  2013-01-14 16:45:42
2069  2013-01-14 16:45:43
signel 17 received
     child end pid 2068, status 0
2069  2013-01-14 16:45:44
signel 17 received
     child end pid 2069, status 0

다시 여러 프로세스가 병렬로 실행되고 있으며 약 10초 동안 실행한 후 ps -ef | grep php를 사용하여 실행 중인 프로세스를 확인합니다
lqling 2066 1388 0 16:45 pts/1 00:00:00 php -f t5.php
이는 상위 프로세스이고 하위 프로세스는 재활용되었습니다.


하위 프로세스 종료 상태

pcntl_waitpid(-1, $status, WNOHANG) $status

하위 프로세스의 종료 상태를 반환합니다.


창 아래의 멀티스레딩

Windows 시스템은 pcntl 기능을 지원하지 않습니다. 다행스럽게도 내부 멀티 스레드를 사용하여 여러 링크에 액세스하는 컬_멀티_exec() 도구가 있으며 각 링크를 작업으로 사용할 수 있습니다.

스크립트 test1.php 작성

<&#63;php
  date_default_timezone_set( 'Asia/Chongqing');
  $tasks = array(
     'http://localhost/feedbowl/t2.php&#63;job=task1',
     'http://localhost/feedbowl/t2.php&#63;job=task2',
     'http://localhost/feedbowl/t2.php&#63;job=task3'
  );
  $mh = curl_multi_init();
  foreach ($tasks as $i => $task){
     $ch[$i] = curl_init();
     curl_setopt($ch[$i], CURLOPT_URL, $task);
     curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);
     curl_multi_add_handle($mh, $ch[$i]);
  }
  do {$mrc = curl_multi_exec($mh,$active); } while ($mrc == CURLM_CALL_MULTI_PERFORM);
  while ($active && $mrc == CURLM_OK) {
     if (curl_multi_select($mh) != -1) {
      do {$mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM);
     }
  }
  // completed, checkout result
  foreach ($tasks as $j => $task){
     if (curl_error($ch[$j])){
       echo "task ${j} [$task ] error " , curl_error($ch[$j]), "\r\n" ;
     } else {
       echo "task ${j} [$task ] get: \r\n" , curl_multi_getcontent($ch[$j]), "\r\n" ;
     }
  }
&#63;>

test2.php 스크립트 작성

<&#63;php
  date_default_timezone_set( 'Asia/Chongqing');
  echo "child start, pid ", getmypid(), "\r\n" ;
  for ($i=0; $i<5; ++$i){
     beep();
  }
  exit (0);
  // ***
  function beep(){
    echo getmypid(), "\t" , date('Y-m-d H:i:s' , time()), "\r\n";
    sleep(1);
  }
&#63;>

명령줄에서 실행

#php -f test1.php &

출력결과

task 0 [http://localhost/feedbowl/t2.php&#63;job=task1] get:
child start, pid 5804
5804  2013-01-15 20:22:35
5804  2013-01-15 20:22:36
5804  2013-01-15 20:22:37
5804  2013-01-15 20:22:38
5804  2013-01-15 20:22:39

task 1 [http://localhost/feedbowl/t2.php&#63;job=task2] get:
child start, pid 5804
5804  2013-01-15 20:22:35
5804  2013-01-15 20:22:36
5804  2013-01-15 20:22:37
5804  2013-01-15 20:22:38
5804  2013-01-15 20:22:39

task 2 [http://localhost/feedbowl/t2.php&#63;job=task3] get:
child start, pid 5804
5804  2013-01-15 20:22:35
5804  2013-01-15 20:22:36
5804  2013-01-15 20:22:37
5804  2013-01-15 20:22:38
5804  2013-01-15 20:22:39

인쇄된 시간을 보면 여러 작업이 거의 동시에 실행되고 있는 것을 볼 수 있습니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.