最近开发一个项目,需要在一个周期内执行许多个类似的任务。
印象中的PHP无法异步执行代码,所以计划使用PHP格式化MySQL的数据,然后提交给Bash Shell Script处理,毕竟在Bash Shell下,可以利用许多GNU程序的组合来实现我的需求,更重要的是在命令末尾加一个”&”符号,即可把任务丢到后台执行,立刻开始下一个任务,最后一切就绪后再提交结果给PHP收尾。
不得不说这是一个很糟糕的方案,虽然数据经过PHP初步格式化,但Shell Script仍然要使用一部分文字处理工具处理数据,其中可能因为某个字段数据比较特殊而出错,由于执行任务需要调用多个程序,效率低下,部分程序之间要使用管道,这些都增加了大量的服务器CPU以及物理内存资源的消耗,更烦的是要循环查询是否所有线程都已经结束工作……
为了让任务能高效率完成,于是寻找了一下有关PHP多线程的解决方案,发现原来PHP有一个提供多线程功能的模块——pthreads。
PHP官方对pthreads模块的介绍:
pthreads is an Object Orientated API that allows user-land multi-threading in PHP. It includes all the tools you need to create multi-threaded applications. PHP applications can create, read, write, execute and synchronize with Threads, Workers and Threaded objects.
介绍说pthreads模块提供了所有构建多线程PHP应用程序所需的工具,果然是世界上最好的语言。
根据PHP官方的文档,使用pthreads模块,需要开启线程安全功能,也就是编译PHP的时候,必须加上参数–enable-maintainer-zts。
此外,开启pthreads模块后,无法使用CGI模式运行PHP。
从PECL可以取得pthreads源码,继而进行编译安装,如果安装了php pear,还可以通过pecl命令安装:
1 |
pecl install pthreads |
下面说说pthreads模块的使用。
这是pthreads模块提供的Thread类的概要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
Thread extends Threaded implements Countable , Traversable , ArrayAccess { /* 方法 */ public void detach ( void ) public integer getCreatorId ( void ) public static Thread getCurrentThread ( void ) public static integer getCurrentThreadId ( void ) public integer getThreadId ( void ) public static mixed globally ( void ) public boolean isJoined ( void ) public boolean isStarted ( void ) public boolean join ( void ) public void kill ( void ) public boolean start ([ integer $options ] ) /* 继承的方法 */ public array Threaded::chunk ( integer $size , boolean $preserve ) public integer Threaded::count ( void ) public bool Threaded::extend ( string $class ) public Threaded Threaded::from ( Closure $run [, Closure $construct [, array $args ]] ) public array Threaded::getTerminationInfo ( void ) public boolean Threaded::isRunning ( void ) public boolean Threaded::isTerminated ( void ) public boolean Threaded::isWaiting ( void ) public boolean Threaded::lock ( void ) public boolean Threaded::merge ( mixed $from [, bool $overwrite ] ) public boolean Threaded::notify ( void ) public boolean Threaded::pop ( void ) public void Threaded::run ( void ) public mixed Threaded::shift ( void ) public mixed Threaded::synchronized ( Closure $block [, mixed $... ] ) public boolean Threaded::unlock ( void ) public boolean Threaded::wait ([ integer $timeout ] ) } |
可以看到Thread是继承了Threaded类,因此我们要使用多线程功能时,直接需要创建一个继承于Thread的类即可:
1 2 3 4 5 |
<?php class myclass extends Thread { // Here gose the code } |
其中run()方法与start()方法是关键。
在需要创建线程执行任务时,调用的是start()方法,而start()又会调用run()方法。
例:
1 2 3 4 5 6 7 8 9 10 11 |
<?php class myclass extends Thread { // Start方法所执行的方法 public function run() { echo rand()."\n"; } } $myobj = new myclass(); // 创建一个名为myobj的对象 $myobj->start(); // 调用myobj对象的start()方法,start()方法会调用run()方法 |
上面的代码,会另起一个线程,输出一个随机数字。
如果我要在十秒后才输出这个随机数字,而且要输出100次,传统来说,至少需要1000秒才能完成输出任务,但使用线程,可以让所有输出几乎同时在十秒完成。
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php class myclass extends Thread { // Start方法所执行的方法 public function run() { sleep(10); // 延迟十秒 echo rand()."\n"; } } for ($i = 0; $i < 100; $i++) { $myobj[$i] = new myclass(); // 创建一个名为myobj的对象 $myobj[$i]->start(); // 调用myobj对象的start()方法,start()方法会调用run()方法 } |
上面的代码,使用了数组,每个数组元素都是一个myclass类的对象,这些对象会同时(虽然创建对象,开始线程等操作也需要时间,但时间非常短,可以忽略)创建一百个线程,这一百个线程同时开始十秒的延迟,十秒后同时输出一个数字:
由于使用的是线程模式,创建一个PHP进程即可处理所有线程,现在无需额外格式化数据,使用PHP的函数实现了所需的功能,相比之前不仅多Bash进程,还多执行各种杂七杂八程序的方案,任务执行效率大大提高。