我找到了这个PECL package called threads,但还没有发布。 PHP网站上没有任何内容。
答案 0 :(得分:175)
来自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利用它的生产就绪和测试功能,以便在添加更多isn'时可以充分利用我们拥有的功能。总是一个选项,而且从来没有真正需要很多任务。
对于那些希望探索它的人来说,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可以在定义的API之外操作TsHashTable。
只有在复制操作发生时,锁定才会按照合理的顺序释放,锁定才会被释放。
这意味着:
发生写入时,不仅要保持读写锁定,还要保持读取和写入锁定 额外的访问锁定。桌子本身被锁定,没有 另一个上下文可以锁定,读取,写入或影响它的可能方式。
当发生读取时,不仅读取锁定,而且读取锁定 额外的访问锁定,表也被锁定。
没有两个上下文可以物理地或同时地从对象存储中访问相同的数据,但是在任何具有引用的上下文中进行的写入都将影响在具有引用的任何上下文中读取的数据。
这是无共享的架构,唯一的存在方式是共存。那些有点精明的人会看到,这里有很多复制,他们会怀疑这是不是一件好事。在动态运行时内进行了大量的复制,这是动态语言的动态。 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执行的环境中,为了提供稳定的环境,需要一些限制和限制。
答案 1 :(得分:48)
以下是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/
答案 2 :(得分:40)
我没有任何可用的东西。下一个最好的事情就是让一个脚本通过CLI执行另一个脚本,但这有点简陋。根据您要做的事情以及它的复杂程度,这可能是也可能不是一种选择。
答案 3 :(得分:24)
简而言之:是的,php中有多线程,但你应该使用多处理。
关于线程和进程的区别总是有点混乱,所以我将很快描述两者:
您可以通过使用php创建新进程(也包含新线程)来实现并行计算。如果您的线程不需要太多的通信或同步,那么这是您的选择,因为这些进程是隔离的,不会干扰彼此的工作。即使一个人崩溃,这与其他人无关。如果你确实需要很多沟通,你应该阅读“多线程”或者 - 遗憾的是 - 考虑使用另一种编程语言,因为进程间通信和同步引入了很多肤色。
在php中,您有两种方法可以创建新流程:
让操作系统为您执行:您可以告诉操作系统创建一个新进程并在其中运行一个新的(或相同的)php脚本。
对于 linux ,您可以使用以下内容或考虑Darryl Hein's answer:
$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()使用分叉的可能性。有关如何执行此操作的详细教程可以找到here但我强烈建议不要使用它,因为fork is a crime against humanity,尤其是对oop。
使用多线程,所有线程共享其资源,因此您可以轻松地在它们之间进行通信并同步它们,而不会产生大量开销。另一方面,你必须知道你在做什么,因为竞争条件和死锁很容易产生但很难调试。
标准php不提供任何多线程,但实际上有一个(实验性)扩展 - pthreads。它的api文档甚至进入了php.net。 有了它,你可以像real programming languages :-)那样做一些事情:
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上有一个installation guide。
对于 windows ,现在有一个:
编辑[phpDirectory] /php.ini并插入以下行
extension=php_pthreads.dll
使用上面的脚本进行测试,并在评论所在位置稍稍休息一下。
现在大的但是:虽然这确实有效,但php最初并不是为多线程而制作的。有一个线程安全的PHP版本,从v5.4开始它似乎几乎没有bug,但在多线程环境中使用php仍然是discouraged in the php manual(但也许他们只是没有更新他们的手册这个,但是)。一个更大的问题可能是许多常见的extensions are not thread-safe。所以你可能会得到这个php扩展的线程,但是你依赖的函数仍然不是线程安全的,所以你可能会在你自己没有编写的代码中遇到竞争条件,死锁等...
答案 4 :(得分:17)
您可以使用pcntl_fork()来实现与线程类似的功能。从技术上讲,它是独立的进程,因此两者之间的通信并不像线程那么简单,我相信如果apache调用PHP,它将无法工作。
答案 5 :(得分:13)
如果有人关心,我已经恢复了 php_threading (与线程不同,但类似)并且我实际上已经达到了它的工作(有点)!
答案 6 :(得分:7)
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);
}
答案 7 :(得分:6)
有一个基于PThreads开发的线程扩展,在https://github.com/krakjoe/pthreads看起来很有希望
答案 8 :(得分:6)
只是一个更新,似乎PHP人员正在努力支持线程,现在可用。
以下是指向它的链接: http://php.net/manual/en/book.pthreads.php
答案 9 :(得分:5)
我有一个PHP线程类,它已经在生产环境中运行了两年多。
编辑:现在可以作为作曲家库和我的MVC框架的一部分,Hazaar MVC。
答案 10 :(得分:2)
我知道这是一个老问题,但您可以查看http://phpthreadlib.sourceforge.net/
双向通信,支持Win32,无需扩展。
答案 11 :(得分:1)
曾经从techdivision那里听说过appserver
吗?
它是用PHP编写的,用作管理多线程php应用程序的多线程的appserver。仍处于测试阶段,但非常谨慎。
答案 12 :(得分:-3)
有一个名为ticks的相当模糊且很快被弃用的功能。我唯一使用它的方法是允许脚本捕获SIGKILL(Ctrl + C)并正常关闭。