深入探究PHP的多进程编程方法_PHP
子进程的创建
一般的子进程的写法是:
<?php $pid = pcntl_fork(); if($pid == -1){ //创建失败 die('could not fork'); } else{ if($pid){ //从这里开始写的代码是父进程的 exit("parent!"); } else{ //子进程代码,为防止不停的启用子进程造成系统资源被耗尽的情况,一般子进程代码运行完成后,加入exit来确保子进程正常退出。 exit("child"); } } ?>
上边的代码如果创建子进程成功的话,系统就有了2个进程,一个为父进程,一个为子进程,子进程的id号为$pid。在系统运行到$pid = pcntl_fork();时,在这个地方进行分支,父子进程各自开始运行各自的程序代码。代码的运行结果是parent 和child,很奇怪吧,为什么一个if和else互斥的代码中,都输出了结果?其实是像上边所说的,代码在pcntl_fork时,一个父进程运行parent,一个子进程运行了child。在代码结果上就显示了parent和child。至于谁先谁后的问题,这得要看系统资源的分配了。
如果需要起多个进程来处理数据,可以根据数据的数量,按照约定好的数量比如说1000条一个进程来起子进程。使用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,在子进程返回值是0,-1表示创建进程失败。跟C非常相似。
测试脚本 test.php
<?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); } ?>
用命令行运行
#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. 非阻塞方式
阻塞方式失去了多进程的并行性。还有一种方法,既可以回收已经结束的子进程,又可以并行。这就是非阻塞的方式。
修改脚本:
<?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); } ?>
用命令行运行
#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下多线程
windows系统不支持pcntl函数,幸好有curl_multi_exec()这个工具,利用内部的多线程,访问多个链接,每个链接可以作为一个任务。
编写脚本 test1.php
<?php date_default_timezone_set( 'Asia/Chongqing'); $tasks = array( 'http://localhost/feedbowl/t2.php?job=task1', 'http://localhost/feedbowl/t2.php?job=task2', 'http://localhost/feedbowl/t2.php?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" ; } } ?>
编写脚本 test2.php
<?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); } ?>
用命令行运行
#php -f test1.php &
输出结果
task 0 [http://localhost/feedbowl/t2.php?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?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?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
从打印的时间看到,多个任务几乎是同时运行的。

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

Session_Start ()로 여러 통화를하면 경고 메시지와 가능한 데이터 덮어 쓰기가 발생합니다. 1) PHP는 세션이 시작되었다는 경고를 발행합니다. 2) 세션 데이터의 예상치 못한 덮어 쓰기를 유발할 수 있습니다. 3) Session_status ()를 사용하여 반복 통화를 피하기 위해 세션 상태를 확인하십시오.

AI는 작곡가 사용을 최적화하는 데 도움이 될 수 있습니다. 특정 방법에는 다음이 포함됩니다. 1. 종속성 관리 최적화 : AI는 종속성을 분석하고 최상의 버전 조합을 권장하며 충돌을 줄입니다. 2. 자동화 된 코드 생성 : AI는 모범 사례를 준수하는 composer.json 파일을 생성합니다. 3. 코드 품질 향상 : AI는 잠재적 인 문제를 감지하고 최적화 제안을 제공하며 코드 품질을 향상시킵니다. 이러한 방법은 기계 학습 및 자연어 처리 기술을 통해 구현되어 개발자가 효율성과 코드 품질을 향상시킬 수 있도록 도와줍니다.

session_start () iscrucialinphpformanagingUsersessions.1) itiniteSanewsessionifnoneexists, 2) ResumesAnxistessions, and3) setSasessionCookieForContInuityAcrosrequests, enablingplicationsirecationSerauthenticationAndpersonalizestContent.

MySQL 기능은 데이터 처리 및 계산에 사용될 수 있습니다. 1. 기본 사용에는 문자열 처리, 날짜 계산 및 수학 연산이 포함됩니다. 2. 고급 사용에는 복잡한 작업을 구현하기 위해 여러 기능을 결합하는 것이 포함됩니다. 3. 성능 최적화를 위해서는 WHERE 절에서 기능 사용 및 GroupBy 및 임시 테이블 사용을 피해야합니다.

HTML5는 5 가지 주요 개선 사항을 제공합니다. 1. 시맨틱 태그는 코드 선명도 및 SEO 효과를 향상시킵니다. 2. 멀티미디어 지원은 비디오 및 오디오 임베딩을 단순화합니다. 3. 형태 향상은 검증을 단순화한다. 4. 오프라인 및 로컬 스토리지는 사용자 경험을 향상시킵니다. 5. 캔버스 및 그래픽 기능은 웹 페이지의 시각화를 향상시킵니다.

Composer는 PHP의 종속성 관리 도구이며 Composer.json 파일을 통해 프로젝트 종속성을 관리합니다. 1) 종속성 정보를 얻기 위해 Composer.json을 구문 분석합니다. 2) 종속성 트리를 형성하기위한 종속성; 3) Packagist에서 공급 업체 디렉토리로 종속성을 다운로드하여 설치합니다. 4) Composer.Lock 파일을 생성하여 팀 일관성 및 프로젝트 유지 관리 가능성을 보장하기 위해 종속성 버전을 잠그십시오.

MySQL에서 문자 세트 및 콜라주를 구성하는 방법은 다음과 같습니다. 1. 서버 수준에서 문자 세트 및 콜라주 설정 : setNames'Utf8 '; setcharactersetutf8; setCollation_connection = 'utf8_general_ci'; 2. 특정 문자 세트 및 콜라주를 사용하는 데이터베이스를 만듭니다. createAbaseexample_DBCHARACTERSETUTF8COLLATEUTF8_GENERAL_CI; 3. 테이블을 만들 때 문자 세트 및 콜라주를 지정하십시오 : CreateTableAmplipt_table (idint

Typetraits는 컴파일 타임 유형 확인 및 작동에 C에서 사용되어 코드 유연성 및 유형 안전성을 향상시킵니다. 1) 유형 판단은 STD :: IS_INTEGRAL 및 STD :: IS_FLOATING_POINT를 통해 수행되므로 효율적인 유형 확인 및 출력을 달성합니다. 2) std :: is_trivicial_copyable을 사용하여 벡터 복사를 최적화하고 유형에 따라 다른 사본 전략을 선택하십시오. 3) 컴파일 타임 의사 결정, 유형 안전, 성능 최적화 및 코드 복잡성에주의하십시오. 타이피트 라이트를 합리적으로 사용하면 코드 품질을 크게 향상시킬 수 있습니다.
