我发现这个PECL包叫做线程,但还没有发布.PHP网站上没有任何内容.
从pthreads扩展的PHP手册:
pthreads是一个面向对象的API,允许PHP中的用户域多线程.它包括创建针对Web或控制台的多线程应用程序所需的所有工具.PHP应用程序可以创建,读取,写入,执行和与Threads,Workers和Stackables同步.
听起来令人难以置信,这是完全正确的.今天,PHP可以为那些希望尝试它的人提供多线程.
PHP4的第一个版本,2000年5月22日,PHP附带了一个线程安全体系结构 - 一种在多线程SAPI(服务器API)环境中在不同线程中执行它的解释器的多个实例的方法.在过去的13年中,这种架构的设计得到了保持和发展:从那以后,它一直在世界上最大的网站上进行生产使用.
用户土地中的线程从未成为PHP团队关注的问题,今天仍然如此.您应该明白,在PHP开展业务的世界中,已经有一种定义的扩展方法 - 添加硬件.多年来PHP已经存在,硬件已经变得更便宜和更便宜,因此这对PHP团队来说变得越来越少.虽然它越来越便宜,但它也变得更加强大; 今天,我们的手机和平板电脑具有双核和四核架构以及大量的RAM,我们的台式机和服务器通常有8或16个核心,16和32千兆字节的RAM,尽管我们可能并不总是有两个在预算范围内,有两个桌面对我们大多数人来说很少有用.
另外,PHP是为非程序员编写的,它是许多业余爱好者的母语.PHP之所以如此容易采用,是因为它是一种易于学习和编写的语言.PHP之所以如此可靠,是因为其设计中的大量工作以及PHP小组做出的每一个决定.经过这么多年,它的可靠性和纯粹的伟大使它保持在聚光灯下; 它的竞争对手已经落到了时间或压力之下.
对于大多数人来说,多线程编程并不容易,即使使用最连贯可靠的API,也需要考虑不同的事情,以及许多误解.PHP小组不希望用户陆地多线程成为核心功能,它从未得到过认真的关注 - 这是正确的.PHP对于每个人来说都不应该是复杂的.
考虑到所有因素,允许PHP利用它的生产就绪和测试功能,以便能够充分利用我们所拥有的功能,当添加更多并不总是一个选项时,仍然有好处.从来没有真正需要任务.
对于那些希望探索它的人来说,pthreads可以实现一个允许用户多线程PHP应用程序的API.它的API正在进行中,并指定了稳定性和完整性的beta级别.
众所周知,PHP使用的某些库不是线程安全的,程序员应该清楚pthreads不能改变它,并且不会尝试尝试.但是,任何线程安全的库都是可用的,就像在解释器的任何其他线程安全设置中一样.
pthreads利用Posix线程(即使在Windows中),程序员创建的是真正的执行线程,但是对于那些有用的线程,他们必须知道PHP - 能够执行用户代码,共享变量并允许有用的通信方式(同步).因此,每个线程都是使用解释器的实例创建的,但是在设计上,它的解释器与解释器的所有其他实例隔离 - 就像多线程服务器API环境一样.pthreads试图以一种安全和安全的方式弥合差距.C中线程程序员的许多问题都不适用于pthreads的程序员,按照设计,pthreads是在读取时复制并在写入时复制(RAM很便宜),因此没有两个实例操纵相同的物理数据,但它们都可以影响另一个线程中的数据.PHP可能在其核心编程中使用线程不安全功能这一事实完全无关紧要,用户线程,并且它的操作是完全安全的.
为什么要在写入时复制读取和复制:
public function run() { ... (1) $this->data = $data; ... (2) $this->other = someOperation($this->data); ... } (3) echo preg_match($pattern, $replace, $thread->data);
(1)当在pthreads对象数据存储上保持读取和写入锁定时,数据将从其在内存中的原始位置复制到对象存储库.pthreads不调整变量的引用计数,如果没有进一步的引用,Zend能够释放原始数据.
(2)someOperation的参数引用了对象存储,存储的原始数据(它本身就是(1)的结果的副本)再次被引擎复制到zval容器中,而这种情况下会发生读锁定对象存储,锁被释放,引擎可以执行该功能.创建zval时,它的引用计数为0,使引擎在完成操作时释放副本,因为不存在对它的其他引用.
(3)preg_match的最后一个参数引用数据存储,获得读锁定,将(1)中的数据集复制到zval,同样使用引用计数0.锁定被释放,对preg_match的调用操作于数据副本,本身就是原始数据的副本.
要知道的事情:
存储数据的对象存储的哈希表(线程安全)
基于Zend提供的PHP附带的TsHashTable.
对象存储具有读写锁定,为TsHashTable提供了额外的访问锁定,这样如果需要(并且确实如此,var_dump/print_r,直接访问属性,因为PHP引擎想要引用它们)pthreads可以操作TsHashTable在定义的API之外.
锁定仅在复制操作发生时保持,当复制已经按照合理的顺序释放锁定时.
这意味着:
发生写入时,不仅要保持读写锁定,还要进行额外的访问锁定.表本身被锁定,其他上下文无法锁定,读取,写入或影响它.
当发生读取时,不仅读取锁定被保持,而且附加的访问锁定也被锁定,表格也被锁定.
没有两个上下文可以物理地或同时地从对象存储中访问相同的数据,但是在任何具有引用的上下文中进行的写入将影响在具有引用的任何上下文中读取的数据.
这是没有共享的架构,唯一的存在方式是共存.那些有点精明的人会看到,这里有很多复制,他们会怀疑这是不是一件好事.在动态运行时内进行了大量的复制,这是动态语言的动态.pthreads是在对象层面实现的,因为可以在一个对象上获得良好的控制,但是方法 - 程序员执行的代码 - 具有另一个上下文,没有锁定和复制 - 本地方法范围.在pthreads对象的情况下,对象范围应被视为在上下文之间共享数据的一种方式,这就是它的目的.考虑到这一点,您可以采用技术来避免锁定对象存储,除非有必要,例如将本地范围变量传递给线程对象中的其他方法,而不是在执行时从对象存储区复制它们.
大多数可用于PHP的库和扩展都是围绕第三方的薄包装,PHP核心功能在某种程度上是相同的.pthreads不是Posix Threads的薄包装器; 它是一个基于Posix线程的线程API.在PHP中实现Threads是没有意义的,它是用户不理解或不能使用的.没有理由不知道互斥体是什么或者做什么的人不应该在技能和资源方面利用他们拥有的所有东西.对象的功能类似于对象,但只要两个上下文发生碰撞,pthread就能提供稳定性和安全性.
任何使用java工作的人都会看到pthreads对象和java中的线程之间的相似之处,那些人们毫无疑问会看到一个名为ConcurrentModificationException的错误 - 因为如果两个线程写入相同的物理数据,这听起来是java运行时引发的错误同时.我理解为什么它存在,但令我感到困惑的是,资源和它们一样便宜,再加上运行时能够在用户可以实现安全的唯一时间检测到并发性,它选择了在运行时抛出可能致命的错误,而不是管理执行和访问数据.
pthreads不会发出这样的愚蠢错误,我认为编写API是为了使线程稳定,并尽可能兼容.
多线程与使用新数据库不同,应该密切关注手册中的每个单词和pthreads附带的示例.
最后,从PHP手册:
pthreads是,并且是一个非常好的结果的实验.其任何限制或功能可能随时发生变化; 这就是实验的本质.它的局限性 - 通常由实施强加 - 存在是有充分理由的; pthreads的目的是为任何级别的PHP中的多任务提供可用的解决方案.在pthreads执行的环境中,为了提供稳定的环境,需要一些限制和限制.
以下是Wilco建议的一个例子:
$cmd = 'nohup nice -n 10 /usr/bin/php -c /path/to/php.ini -f /path/to/php/file.php action=generate var1_id=23 var2_id=35 gen_id=535 > /path/to/log/file.log & echo $!'; $pid = shell_exec($cmd);
基本上这会在命令行执行PHP脚本,但会立即返回PID,然后在后台运行.(echo $!确保除PID之外不会返回任何其他内容.)这允许您的PHP脚本继续或退出(如果需要).当我使用它时,我已将用户重定向到另一个页面,每隔5到60秒进行一次AJAX调用以检查报告是否仍在运行.(我有一个表来存储gen_id及其相关的用户.)检查脚本运行以下命令:
exec('ps ' . $pid , $processState); if (count($processState) < 2) { // less than 2 rows in the ps, therefore report is complete }
这里有一篇关于这种技术的简短帖子:http://nsaunders.wordpress.com/2007/01/12/running-a-background-process-in-php/
我没有任何可用的东西.下一个最好的事情就是让一个脚本通过CLI执行另一个脚本,但这有点简陋.根据您要做的事情以及它的复杂程度,这可能是也可能不是一种选择.
简而言之:是的,PHP中存在多线程,但您应该使用多处理.
关于线程和进程的区别总是有点混乱,所以我将很快描述两者:
甲线程是CPU将处理命令序列.它包含的唯一数据是程序计数器.每个CPU内核一次只处理一个线程,但可以通过调度在不同的线程执行之间切换.
甲过程是一组共享资源.这意味着它由一部分内存,变量,对象实例,文件句柄,互斥体,数据库连接等组成.每个进程还包含一个或多个线程.同一进程的所有线程共享其资源,因此您可以在另一个中创建的一个线程中使用变量.如果这些线程是两个不同进程的一部分,那么它们不能直接访问彼此的资源.在这种情况下,您需要通过例如管道,文件,套接字进行进程间通信 ......
您可以通过使用php创建新进程(也包含新线程)来实现并行计算.如果您的线程不需要太多的通信或同步,那么这是您的选择,因为这些进程是隔离的,不会干扰彼此的工作.即使一个人崩溃,这与其他人无关.如果你确实需要很多沟通,你应该阅读"多线程"或者 - 遗憾的是 - 考虑使用另一种编程语言,因为进程间通信和同步引入了很多肤色.
在php中,您有两种方法可以创建新流程:
让操作系统为您完成:您可以告诉您的操作系统创建一个新进程并在其中运行一个新的(或相同的)PHP脚本.
对于linux,您可以使用以下内容或考虑Darryl Hein的答案:
$cmd = 'nice php script.php 2>&1 & echo $!'; pclose(popen($cmd, 'r'));
对于Windows,您可以使用此:
$cmd = 'start "processname" /MIN /belownormal cmd /c "script.php 2>&1"'; pclose(popen($cmd, 'r'));
使用fork自己动手:php还提供了通过函数pcntl_fork()使用分叉的可能性.关于如何做到这一点的好教程可以在这里找到,但我强烈建议不要使用它,因为fork是一种危害人类的罪行,尤其是对oop 的犯罪.
使用多线程,所有线程共享其资源,因此您可以轻松地在它们之间进行通信并同步它们,而不会产生大量开销.另一方面,你必须知道你在做什么,因为竞争条件和死锁很容易产生但很难调试.
标准的PHP不提供任何多线程,但实际上有一个(实验性)扩展--pthreads.它的api文档甚至进入了php.net.有了它,你可以在真正的编程语言中做一些事情:-)像这样:
class MyThread extends Thread { public function run(){ //do something time consuming } } $t = new MyThread(); if($t->start()){ while($t->isRunning()){ echo "."; usleep(100); } $t->join(); }
对于linux,在stackoverflow上有一个安装指南.
对于Windows现在有一个:
首先,您需要php的线程安全版本.
您需要pthreads及其php扩展的预编译版本.它们可以在这里下载.确保下载与php版本兼容的版本.
将php_pthreads.dll(从您刚下载的zip)复制到您的php扩展文件夹([phpDirectory] / ext).
将pthreadVC2.dll复制到[phpDirectory](根文件夹 - 而不是扩展文件夹).
编辑[phpDirectory] /php.ini并插入以下行
extension=php_pthreads.dll
使用上面的脚本测试它,并在注释所在的位置进行一些睡眠或其他操作.
而现在最重要但是:虽然这确实有效,但php最初并不是为多线程而制作的.有一个线程安全版本的PHP和v5.4似乎几乎没有bug,但在多线程环境中使用PHP仍然不鼓励PHP手册(但也许他们只是没有更新他们的手册这个,但是).一个更大的问题可能是很多常见的扩展都不是线程安全的.所以你可能会得到这个php扩展的线程,但是你所依赖的函数仍然不是线程安全的,所以你可能会在你自己没有编写的代码中遇到竞争条件,死锁等等......
您可以使用pcntl_fork()来实现类似于线程的东西.从技术上讲,它是独立的进程,因此两者之间的通信并不像线程那么简单,我相信如果apache调用PHP,它将无法工作.
如果有人关心,我已经恢复了php_threading(与线程不同,但类似),我实际上已经到了它工作的地步(有点)!
项目页面
下载(适用于Windows PHP 5.3 VC9 TS)
例子
自述
pcntl_fork()
是你正在寻找的,但它的过程不分线.所以你会遇到数据交换的问题.解决它们你可以使用phps信号量函数(http://www.php.net/manual/de/ref.sem.php)消息队列开头可能比共享内存段更容易一些.
无论如何,我正在开发的一个Web框架中使用的策略,它正在加载一个网页的资源密集型块(可能与外部请求)并行:我正在做一个作业队列,知道我在等什么数据,然后我分叉关闭每个过程的工作.完成后,他们将数据存储在父进程可以访问的唯一键下的apc缓存中.一旦每个数据都存在,它就会继续.我使用简单usleep()
等待,因为在apache中不可能进行进程间通信(孩子们将失去与父母的联系并成为僵尸......).所以这让我最后一件事:自杀每个孩子都很重要!还有一些类可以分叉进程但保留数据,我没有检查它们,但是zend框架有一个,它们通常做得慢而且可靠.你可以在这里找到它:
http://zendframework.com/manual/1.9/en/zendx.console.process.unix.overview.html
我认为他们使用shm段!最后但并非最不重要的是,这个zend网站上有一个错误,例子中的小错误.
while ($process1->isRunning() && $process2->isRunning()) { sleep(1); } should of course be: while ($process1->isRunning() || $process2->isRunning()) { sleep(1); }
有一个基于PThreads开发的线程扩展,看起来很有希望在https://github.com/krakjoe/pthreads
只是一个更新,它似乎PHP家伙正在努力支持线程,它现在可用.
以下是它的链接:http: //php.net/manual/en/book.pthreads.php
我有一个PHP线程类,它已经在生产环境中运行了两年多,现在已经完美无缺.
编辑:现在可以作为作曲家库和我的MVC框架Hazaar MVC的一部分.
请参阅:https://git.hazaarlabs.com/hazaar/hazaar-thread