php多进程核心
1.创建一个子进程
$pid = pcntl_fork();
//父进程和子进程都会执行下面代码
if ($pid > 0) {
//父进程会得到子进程号,所以这里是父进程执行的逻辑
pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程
} elseif ($pid == 0) {
//子进程得到的$pid为0, 所以这里是子进程执行的逻辑
} else {
//错误处理:创建子进程失败时返回-1
die(‘could not fork’);
}
//挂起主进程,等待一个子进程退出或被信号打断
int pcntl_wait ( int &$status [, int $options = 0 ] )
2.创建多个进程
for ($i = 0; $i 0) {
//父进程会得到子进程号,所以这里是父进程执行的逻辑
} elseif ($pid == 0) {
//子进程得到的$pid为0, 所以这里是子进程执行的逻辑
exit(0);
} else {
//错误处理:创建子进程失败时返回-1
die(‘could not fork’);
}
}
注意,创建多个子进程的时候,不能在主进程中加入 pcntl_wait($status),主进程执行到pcntl_fork后,会产生一个子进程,主进程和子进程会分开执行,当挂起主进程后,主进程会等待子进程结束后才继续执行下一个循环,所以会导致堵塞 子进程处理结束后必须加上exit(0)终止子进程,否则子进程将继续执行if之后的代码
3.多进程工作实例
for ($i = 0; $i 0) {
//父进程会得到子进程号,所以这里是父进程执行的逻辑
} elseif ($pid == 0) {
//子进程得到的$pid为0, 所以这里是子进程执行的逻辑
call_user_func(‘work_handler’);
exit(0);
} else {
//错误处理:创建子进程失败时返回-1
die(‘could not fork’);
}
}
function work_handler() {
//子进程执行的逻辑
}
信号
涉及到进程管理的几个信号量
//通知前台进程组终止进程
SIGINT
//供用户自定义
SIGUSR1
//供用户自定义
SIGUSR2
1.安装信号处理器
bool pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] )
//声明用于进程信号处理。如果注册了信号处理器,程序会每执行一行代码后自动检查是否有尚未处理的信号
declare(ticks = 1);
//安装信号处理器
pcntl_signal(SIGTERM, “sig_handler”);
pcntl_signal(SIGHUP, “sig_handler”);
pcntl_signal(SIGUSR1, “sig_handler”);
//或者
pcntl_signal(SIGUSR1, array($obj, “do_something”);
信号处理器,顾名思义是当信号产生后(信号产生有多种方式,有用户产生也有系统产生)执行的函数
2.定义信号处理函数
//信号处理函数
function sig_handler($signo)
{
switch ($signo) {
case SIGTERM:
//处理SIGTERM信号
exit;
break;
case SIGHUP:
//处理SIGHUP信号
break;
case SIGUSR1:
echo “Caught SIGUSR1…\n”;
break;
default:
//处理所有其他信号
}
}
//发送信号
bool posix_kill ( int $pid , int $sig )
注意,以上的安装信号和处理信号函数只是绑定发送信号后的处理程序,其实如果需要关闭进程仅需使用posix_kill($pid, SIGINT)
通过信号来控制进程
/**
* 安装信号处理器
* @return void
*/
protected function install_signal()
{
// stop
pcntl_signal(SIGINT, ‘signal_handler’, false);
// reload
pcntl_signal(SIGUSR1, ‘signal_handler’, false);
// status
pcntl_signal(SIGUSR2, ‘signal_handler’, false);
}
/**
* 信号处理函数
* @return void
*/
public function signal_handler($signal) {
switch ($signal) {
// stop
case SIGINT:
stop_all();
break;
// reload
case SIGUSR1:
echo “sreloadtop\n”;
break;
// show status
case SIGUSR2:
echo “status\n”;
break;
}
}
$_worker_pids = array();
//开启多进程
for ($i = 0; $i 0)
{
$_worker_pids[$pid] = $pid;
}
// 子进程
elseif(0 === $pid)
{
// 子进程逻辑
// 这里用0表示正常退出
exit(0);
}
else
{
exit(“fork one worker fail”);
}
}
/**
* 用信号依次关闭子进程
* @return void
*/
public function stop_all()
{
foreach ($_worker_pids as $worker_pid)
{
posix_kill($worker_pid, SIGINT);
}
}
install_signal()安装信号处理器,把信号处理函数signal_handler()和三个信号绑定,当进程出现SIGINT信号的时候,会调用stop_all()函数,stop_all()函数会逐一对$_worker_pids记录的进程发送SIGINT信号
一个多进程类
threads = [];
$this->ignoreErrors = false;
$this->memoryKey = round(microtime(true) * 1000);
}
/**
* Run some code in a thread.
*
* @param callable $func The function to execute
* @param array|mixed $args The arguments (or a single argument) to pass to the function
*
* @return int The pid of the thread created to execute this code
*/
public function call(callable $func, $args = null)
{
$pid = pcntl_fork();
if ($pid == -1) {
throw new \Exception(“Failed to fork”);
}
# If this is the child process, then run the requested function
if (!$pid) {
try {
if ($args === null) {
$func();
} else {
call_user_func_array($func, $args);
}
} catch(\Exception $e) {
$memory = shmop_open($this->memoryKey, “c”, 0644, static::SHARED_MEMORY_LIMIT);
$errors = shmop_read($memory, 0, static::SHARED_MEMORY_LIMIT);
$errors = trim($errors);
if ($errors) {
$errors .= “\n”;
}
$errors .= “Exception: ” . $e->getMessage() . ” (” . $e->getFile() . “:” . $e->getLine() . “)”;
shmop_write($memory, $errors, 0);
shmop_close($memory);
exit(1);
}
# Then we must exit or else we will end up the child process running the parent processes code
die();
}
$this->threads[$pid] = $pid;
return $pid;
}
/**
* Wait for the processes started via call().
*
* @param int $pid The pid to wait for, if none is passed then all threads created by this object will be waited for
*
* @return int The highest return status for each of the processes we’ve waited for
*/
public function wait($pid = null)
{
if ($pid) {
$threads = [$pid];
} else {
$threads = $this->threads;
}
$error = false;
$status = 0;
foreach ($threads as $pid) {
pcntl_waitpid($pid, $status);
if ($status > 0) {
$error = $status;
}
unset($this->threads[$pid]);
}
if (!$this->ignoreErrors && $error) {
$memory = shmop_open($this->memoryKey, “a”, 0, 0);
$errors = shmop_read($memory, 0, static::SHARED_MEMORY_LIMIT);
shmop_delete($memory);
shmop_close($memory);
$error = “An error occurred within a thread, the return code was (” . $error . “)”;
if ($errors = trim($errors)) {
$error .= “\n” . $errors;
}
throw new \Exception($error);
}
return $status;
}
/**
* If no call to wait() is made, then we wait for the threads on destruct
*/
public function __destruct()
{
$this->wait();
}
}