虽然不能创建线程,但是我们可以创建进程,一样可以达到利用多核CPU的目的。

涉及到资源的并发利用问题,多线程、多进程都须要并发安全地利用资源。

大略例子

实验代码

pcntlphpwindowsphp pcntl开启多过程履行义务 Python

ceshi.php

<?php// 开启多个进程,同时实行一个任务function task($startID) { $pid = posix_getpid(); // 获取进程号 printf("进程号:%d,实行任务: %d - %d\n", $startID, $startID + 1000);}for ($i = 0; $i < 5; $i++) { $id = pcntl_fork(); if ($id == -1) { die("pcntl fork failed"); } elseif($id) { // parent process continue; } else { // child process task($i 1000); exit; }}printf("父进程结束\n");

运行结果

php ceshi.php

任务实行结果

上图中我们开启了五个进程,每个进程实行一段任务,隔离了任务,防止不同进程实行相同的任务。

不知道大家有没有把稳到,上图中父进程退出了,为什么还能打印出进程号为 594 号的输出呢。
这是由于子进程继续了父进程打开的文件描述符,如:标准输出(stdout)、标准输入(stdin)、标准缺点(stderr)。
这三个描述符都是对应终真个,以是会输出到终端,便是我们实行命令的窗口。

守护进程实现

上面脚本如果是一个无限循环,日志将会一贯打印,当用户Session终端退出,任务也会随着退出。
这时候我们就须要利用守护进程去实现这个任务进程,守护进程一旦实行,终端关闭与否,任务进程都会运行。
关闭守护进程可以利用kill命令,或者在任务实行完成之后直接退出就可以。

实验代码

ceshi.php

<?php// 开启多个进程,同时实行一个任务function task($startID) { $pid = posix_getpid(); while (true) { file_put_contents($pid."log.txt", "任务{$startID}-". ($startID+1000) ."实行完毕\n", FILE_APPEND); sleep(5); }}$pid = pcntl_fork();if ($pid == -1){ die("pcntl fork failed: first time");} elseif ($pid > 0) {//父进程退出,子进程不是进程组长,以便接下来顺利创建新会话 exit;}posix_setsid(); // 分开终端,设置新的session会话posix_setuid(33); // 设置进程用户posix_setgid(33); // 设置进程组fclose(STDOUT); // 关闭标准输出fclose(STDIN); // 关闭标准输入fclose(STDERR); // 关闭标准缺点cli_set_process_title("ceshi"); // 设置进程名字// 循环创建子进程处理任务for ($i = 0; $i < 5; $i++) { $id = pcntl_fork(); if ($id == -1) { die("pcntl fork failed"); } elseif($id) { // parent process continue; } else { // child process task($i 1000); exit; }}

运行结果

php ceshi.php

守护进程结果截图

守护进程的代码里面已经注释了实现逻辑,截图也已经将关键的命令以及结果做了注释,大家可以结合着看,我这里就不多做先容。

实现守护进程的过程中我自己踩到一个坑,这里分享下,避免踩坑:

在关闭标准输入、标准输出、标准缺点的文件描述符之后,就不要再利用 printf 函数,这个函数会将结果打印到标准输出,如果强行打印会致使程序退出,ps 也查不到干系运行的进程。