php pcntl 进程池_使用 swoole_process 实现 PHP 进程池

swoole_process 主要是用来代替 PHP 的 pcntl 扩展。我们知道 pcntl 是用来进行多进程编程的,而 pcntl 只提供了 fork 这样原始的接口,容易使用错误,并且没有提供进程间通信以及重定向标准输入输出的功能。

而 swoole_process 则提供了比 pcntl 更强大的功能,更易用的API,使PHP在多进程编程方面更加轻松。

本文使用 swoole_process 与 EventLoop 完成一个 php 的进程池,并且支持动态创建新进程。

EventLoop

swoole 有一个 Reactor 线程,这个线程可以说是对 epoll 模型的封装,可以设置 read 事件和 write 事件的监听回调函数。

下面会用到一个函数:

bool swoole_event_add(mixed $sock, mixed $read_callback, mixed $write_callback = null, int $flags = null);

参数1为一个文件描述符,包括swoole_client->$sock、swoole_process->$pipe或者其他 fd(socket_create 创建的资源 , stream_socket_client/fsockopen创建的资源)

参数2为可读事件回调函数

参数3为可写事件回调函数

多进程编程少不了进程之间的通讯,swoole 的进程之间有两种通信方式,一种是消息队列(queue),另一种是管道(pipe)。那么本文使用的是 pipe 的方式。

下面是一个定时向进程池投递任务的例子。

代码:

class ProcessPool{

private $process;

/**

* Worker 进程数组

* @var array

*/

private $process_list = [];

/**

* 正在被使用的进程

* @var array

*/

private $process_use = [];

/**

* 最少进程数量

* @var int

*/

private $min_worker_num = 3;

/**

* 最多进程数量

* @var int

*/

private $max_worker_num = 6;

/**

* 当前进程数量

* @var int

*/

private $current_num;

public function __construct()

{

$this->process = new swoole_process(array($this, 'run'), false, 2);

$this->process->start();

swoole_process::wait();

}

public function run()

{

$this->current_num = $this->min_worker_num;

//创建所有的worker进程

for($i = 0; $i < $this->current_num; $i++){

$process = new swoole_process(array($this, 'task_run'), false, 2);

$pid = $process->start();

$this->process_list[$pid] = $process;

$this->process_use[$pid] = 0;

}

foreach($this->process_list as $process){

swoole_event_add($process->pipe, function ($pipe) use ($process){

$data = $process->read();

var_dump($data . '空闲');

//接收子进程处理完成的信息,并且重置为空闲

$this->process_use[$data] = 0;

});

}

//每秒定时向worker管道投递任务

swoole_timer_tick(1000 ,function ($timer_id){

static $index = 0;

$index = $index + 1;

$flag = true; //是否新建worker

foreach ($this->process_use as $pid => $used){

if($used == 0){

$flag = false;

//标记为正在使用

$this->process_use[$pid] = 1;

// 在父进程内调用write,子进程可以调用read接收此数据

$this->process_list[$pid]->write($index. "hello");

break;

}

}

if($flag && $this->current_num < $this->max_worker_num){

//没有闲置worker,新建worker来处理

$process = new swoole_process(array($this, 'task_run'), false, 2);

$pid = $process->start();

$this->process_list[$pid] = $process;

$this->process_use[$pid] = 1;

$this->process_list[$pid]->write($index. "hello");

$this->current_num++;

}

var_dump('第' .$index. '个任务');

if($index == 10){

foreach($this->process_list as $process){

$process->write("exit");

}

swoole_timer_clear($timer_id);

$this->process->exit();

}

});

}

/**

* 子进程处理

* @param $worker

*/

public function task_run($worker)

{

swoole_event_add($worker->pipe, function($pipe)use($worker){

$data = $worker->read();

var_dump($worker->pid . ':' . $data);

if($data == 'exit'){

$worker->exit();

exit;

}

//模拟耗时任务

sleep(5);

//告诉主进程处理完成

//在子进程内调用write,父进程可以调用read接收此数据

$worker->write($worker->pid);

});

}

}

new ProcessPool();

首先定义几个重要的属性:

$process_list :Worker 进程数组

$process_use:正在被使用的进程

$min_worker_num :最少进程数量

$max_worker_num :最多进程数量

$current_num :当前进程数量

$process : 主进程

在实例化的时候创建主进程,并且运行 run 方法,在 run 方法里面先创建所有的 worker 进程,并且设置为空闲状态。

接着遍历所有的 worker 进程,并且加入 EventLoop 中,设置可读事件,用于接收子进程的空闲信号。

最后每隔一秒向 worker 进程投递任务。动态扩充进程池则在这里实现,如果没有闲置的进程,而此时又有新的任务,则需要动态创建一个新的进程并且置为繁忙状态。由于只模拟了十次任务,则第十个任务完成之后在父进程中发送 exit 使所有子进程退出。

运行效果与图解:

bfc645a965d0

Snipaste_2018-10-27_18-36-11.png

参考链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值