PHP有线程吗?

我发现这个叫做线程的PECL包 ,但是还没有发布。 没有什么是在PHP网站上。

没有什么我知道的。 下一个最好的事情就是简单地通过一个CLI执行另一个脚本,但是这有点简单。 取决于你想要做什么以及它有多复杂,这可能是也可能不是一个选择。

pthreads扩展的PHP手册:

pthreads是一个面向对象的API,允许用户在PHP中进行多线程。 它包含了创建面向Web或控制台的多线程应用程序所需的所有工具。 PHP应用程序可以创建,读取,写入,执行和同步线程,工作者和可堆栈。

这听起来令人难以置信,这是完全正确的。 今天,PHP可以为那些希望尝试的人提供多线程。

PHP4的第一个版本,2000年5月22日,PHP被提供了一个线程安全体系结构 – 一种在多线程SAPI(服务器API)环境中的独立线程中执行它的解释器的多个实例的方法。 在过去的13年里,这个架构的设计一直保持和发展:从那以后,它一直在世界上最大的网站上使用。

在用户的土地上进行线程化并不是PHP团队关心的问题,而且今天仍然如此。 你应该明白,在PHP这个世界上它是业务,已经有一个定义的缩放方法 – 添加硬件。 在PHP已经存在多年的情况下,硬件已经变得更便宜,更便宜,所以这对PHP团队来说越来越不受关注了。 当它变得更便宜时,它也变得更加强大; 今天,我们的手机和平板电脑拥有双核四核架构和大量内存,我们的台式机和服务器通常有8或16核心,16和32千兆字节的RAM,尽管我们可能并不总是能够拥有两个在预算范围内,有两个桌面是很少有用的,我们大多数人。

另外,PHP是为非程序员编写的,它是许多业余爱好者的母语。 PHP很容易被采用的原因是因为它是一种容易学习和编写的语言。 PHP今天如此可靠的原因是因为PHP设计所做的大量工作以及PHP团队做出的每一个决定。 毕竟这些年来,它的可靠性和纯粹的伟大性一直保持在聚光灯下。 竞争对手已经陷入时间或压力。

多线程编程对于大多数人来说并不容易,即使使用最一致和可靠的API,也有不同的想法和许多误解。 PHP团队不希望用户域多线程成为核心功能,它从来没有受到过严肃的关注 – 而且是正确的。 PHP不应该是复杂的,适合每个人。

所有的事情都考虑到了,PHP允许利用它的生产就绪和测试功能,让我们能够充分利用已有的东西,添加更多的东西并不总是一种选择,的任务从来没有真正需要。

对于那些希望探索它的人来说,pthreads可以实现一个允许用户使用多线程PHP应用程序的API。 API是一个非常有用的工作,并且指定了一个beta级别的稳定性和完整性。

一些PHP使用的库不是线程安全的,这是常识,程序员应该清楚pthreads不能改变这个,而不会尝试去尝试。 但是,任何线程安全的库都是可用的,就像解释器的任何其他线程安全设置一样。

pthreads利用Posix Threads(甚至在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不调整变量的refcount,如果没有进一步的引用,Zend能够释放原始数据。

(2)someOperation的参数引用了对象存储,存储的原始数据(它自己是(1)结果的副本)被再次复制到一个zval容器中,在发生这种情况时,读锁定被保持对象存储器,锁被释放并且引擎可以执行该功能。 当创建zval时,它的refcount为0,使引擎在操作完成时释放副本,因为不存在其他引用。

(3)preg_match的最后一个参数引用数据存储,获得读锁,将(1)中设置的数据复制到一个zval,同时引用数为0.锁被释放,对preg_match的调用在数据副本,这本身就是原始数据的副本。

事情要知道:

  • 数据存储在线程安全的对象存储哈希表
    基于Zend提供的PHP附带的TsHashTable。

  • 对象存储具有读写锁定,为TsHashTable提供额外的访问锁定,以便如果需要(和var_dump / print_r直接访问属性,PHP引擎想引用它们),pthread可以操作TsHashTable在定义的API之外。

  • 只有在复制操作发生时,才能保持锁定状态,当复制完成后,锁定将以合理的顺序被释放。

意即:

  • 发生写操作时,不仅会保持读写锁定,还会有一个额外的访问锁定。 表本身被锁定,另一个上下文没有可能锁定,读取,写入或影响它。

  • 当读取发生时,不仅是读取锁定,而且还有额外的访问锁定,表格也被锁定。

没有两个上下文可以物理上或同时访问来自对象库的相同数据,但是在任何具有引用的上下文中进行的写入都将影响在任何具有引用的上下文中读取的数据。

这是没有共享的架构,唯一的存在方式是共存的。 那些有点精明的人会看到,这里有很多复制,他们会怀疑这是否是件好事。 在一个动态的运行时间里,相当多的复制是在动态语言的动态变化中进行的。 pthreads是在对象级别实现的,因为可以通过一个对象获得良好的控制权限,但是方法(程序员执行的代码)具有另一个上下文,没有锁定和复制 – 本地方法范围。 pthreads对象情况下的对象范围应被视为在上下文之间共享数据的一种方式,也就是它的目的。 考虑到这一点,你可以采用技术来避免锁定对象存储,除非必要,比如将本地作用域变量传递给线程对象中的其他方法,而不是在执行时从对象存储中复制。

大多数可用于PHP的库和扩展都是围绕第三方的简单包装,PHP核心功能在某种程度上是相同的。 pthreads不是Posix Threads的简单包装; 它是基于Posix线程的线程API。 在PHP中实现Threads没有意义,它是用户不理解或不能使用的。 没有理由不知道互斥体是什么或者做什么的人不应该利用他们所有的技能和资源。 一个对象的功能就像一个对象,但是当两个上下文碰撞时,pthread提供了稳定性和安全性。

任何在java中工作的人都会看到pthreads对象和java线程之间的相似之处,这些人肯定会看到一个名为ConcurrentModificationException的错误,因为如果两个线程写入相同的物理数据,同时。 我明白为什么它存在,但它让我感到困惑,即使用尽可能便宜的资源,再加上运行时能够在确切且唯一的时间检测到并发性的事实,使用户可以实现安全性,它选择在运行时抛出一个可能致命的错误,而不是管理数据的执行和访问。

没有这样的愚蠢的错误将被pthreads发出,API编写,使线程稳定,并尽可能兼容,我相信。

多线程不像使用新的数据库,应密切注意手册中的每个单词以及pthreads附带的示例。

最后,从PHP手册:

pthreads是,并且是一个有相当好结果的实验​​。 其任何限制或功能可能随时改变; 这是实验的本质。 它的局限性 – 通常由实施强加 – 存在是有原因的; pthreads的目标是提供一个可用的解决方案,在任何级别的PHP多任务。 在线程执行的环境中,为了提供稳定的环境,需要一些限制和限制。

这是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/

简而言之:是的,在PHP中有多线程,但你应该使用多处理。

Backgroud信息:线程与进程

关于线程和进程的区别总是有点混乱,所以我将很快描述两者:

  • 线程是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')); 

用叉子自己做 :php也提供了通过函数pcntl_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的。

对于窗户现在有一个:

  • 首先你需要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似乎几乎没有错误,但在多线程环境中使用PHP仍然不鼓励在PHP手册 (但也许他们只是没有更新他们的手册上这,但)。 一个更大的问题可能是许多常见的扩展不是线程安全的 。 所以你可能会得到这个php扩展的线程,但是你依赖的函数仍然不是线程安全的,所以你可能会遇到竞争条件,死锁等代码中,你没有写自己…

你可以使用pcntl_fork()来实现类似于线程的东西。 从技术上讲,它是单独的进程,所以两者之间的通信并不像线程那样简单,而且我相信如果PHP被apache调用,它将不会工作。

如果有人关心,我已经恢复了php_threading (不同于线程,但类似),我实际上已经到了它的工作点(有点)!

项目页面

下载(适用于Windows的PHP 5.3 VC9 TS)

例子

读我

pcntl_fork()是你正在寻找,但其进程分叉不线程。 所以你会有数据交换的问题。 解决它们你可以使用phps信号量函数( http://www.php.net/manual/de/ref.sem.php )消息队列可能比共享内存段更容易开始。

无论如何,我正在使用一个网络框架,我正在开发的一个策略,加载一个网页的资源密集块(可能与外部请求)并行:我在做一个工作队列,知道什么数据我等待,然后我叉关闭每个过程的工作。 一旦完成,他们将数据存储在父进程可以访问的唯一密钥下的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开发activley,看起来非常有希望在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

我知道这是一个古老的问题,但你可以看看http://phpthreadlib.sourceforge.net/

双向通讯,支持Win32,不需要扩展。

曾经听说过从技术部门的appserver

它是用PHP编写的,作为管理高流量php应用程序的多线程的应用程序服务器。 目前仍处于测试阶段,但非常具有挑战性。

有一个比较模糊的,很快就会被弃用的特性叫做蜱 。 我曾经使用过的唯一方法是让脚本捕捉SIGKILL(Ctrl + C)并优雅地关闭。