PHP 有线程吗?

我找到了这个 称为线程的 PECL 包,但是还没有发布,而且 PHP 网站上也没有任何东西。

143634 次浏览

据我所知,没有什么东西是可用的。接下来最好的方法是通过 CLI 简单地让一个脚本执行另一个脚本,但这有点基础。根据您正在尝试做的事情以及它的复杂程度,这可能是一个选项,也可能不是。

您可以使用 Pcntl _ fork ()来实现类似于线程的功能。从技术上讲,它是独立的进程,因此两者之间的通信不像线程那样简单,而且我相信,如果 Apache 调用 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,然后在后台运行。(回声!确保除了 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/

有一个相当晦涩的、很快就会被废弃的特性,叫做 扁虱。我唯一使用过它的地方就是允许脚本捕获 SIGKILL (Ctrl + C)并优雅地关闭。

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 我觉得他们用的是闪光灯! 最后但并非最不重要的是,在这个 zend 网站上有一个错误,例子中的一个小错误。

while ($process1->isRunning() && $process2->isRunning()) {
sleep(1);
}
should of course be:
while ($process1->isRunning() || $process2->isRunning()) {
sleep(1);
}

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

项目页面

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

例子

自述

我有一个 PHP 线程类,它已经在一个生产环境中完美地运行了两年多了。

编辑: 现在可以作为作曲家库和我的 MVC 框架 Hazaar MVC 的一部分使用。

见: https://git.hazaarlabs.com/hazaar/hazaar-thread

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

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

有一个线程扩展正在积极开发的基础上的 PThreads,看起来非常有前途的 https://github.com/krakjoe/pthreads

只是一个更新,它似乎 PHP 的家伙正在工作的支持线程和它现在可用。

下面是链接: Http://php.net/manual/en/book.pthreads.php

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

背景信息: 线程与进程

线程和进程之间的区别总是有点混淆,因此我将简要介绍两者:

  • 线是 CPU 将处理的一系列命令。它所包含的唯一数据是一个程序计数器。每个 CPU 核心一次只处理一个线程,但是可以通过调度在不同线程的执行之间切换。
  • 程序是一组共享资源。这意味着它由一部分内存、变量、对象实例、文件句柄、互斥锁、数据库连接等组成。每个进程还包含一个或多个线程。同一进程的所有线程都共享其资源,因此您可以在一个线程中使用在另一个线程中创建的变量。如果这些线程是两个不同进程的一部分,那么它们就不能直接访问彼此的资源。在这种情况下,你需要通过例如管道,文件,套接字 行程间通讯..。

多重处理

您可以通过使用 php 创建新进程(也包含一个新线程)来实现并行计算。如果您的线程不需要太多的通信或同步,这是您的选择,因为进程是独立的,不能干扰彼此的工作。就算一个崩溃了,其他人也不用担心。如果你确实需要更多的交流,你应该继续阅读“多线程”,或者——遗憾的是——考虑使用另一种编程语言,因为行程间通讯和同步会带来很多麻烦。

在 php 中,有两种方法可以创建一个新进程:

让操作系统为您做这件事 : 您可以告诉您的操作系统创建一个新进程,并在其中运行一个新的(或相同的) php 脚本。

  • 对于 Linux,你可以使用以下方法或考虑使用 Darryl Hein 的回答:

    $cmd = 'nice php script.php 2>&1 & echo $!';
    pclose(popen($cmd, 'r'));
    
  • for windows you may use this:

    $cmd = 'start "processname" /MIN /belownormal cmd /c "script.php 2>&1"';
    pclose(popen($cmd, 'r'));
    

do it yourself with a fork: php also provides the possibility to use forking through the function pcntl_fork(). A good tutorial on how to do this can be found here but i strongly recommend not to use it, since fork is a crime against humanity and especially against oop.

Multithreading

With multithreading all your threads share their resources so you can easily communicate between and synchronize them without a lot of overhead. On the other side you have to know what you are doing, since race conditions and deadlocks are easy to produce but very difficult to debug.

Standard php does not provide any multithreading but there is an (experimental) extension that actually does - pthreads. Its api documentation even made it into php.net. With it you can do some stuff as you can in real programming languages :-) like this:

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 版本。
  • 您需要 pthread 及其 php 扩展的预编译版本。他们可以下载 给你。请确保您下载了与您的 php 版本兼容的版本。
  • 将 php _ pthreads.dll (从刚才下载的 zip)复制到 php 扩展文件夹([ phpDirectory ]/ext)。
  • 将 pthreadVC2.dll 复制到[ phpDirectory ](根文件夹-而不是扩展文件夹)。
  • 编辑[ phpDirectory ]/php.ini 并插入以下行

    extension=php_pthreads.dll
    
  • Test it with the script above with some sleep or something right there where the comment is.

And now the big BUT: Although this really works, php wasn't originally made for multithreading. There exists a thread-safe version of php and as of v5.4 it seems to be nearly bug-free but using php in a multi-threaded environment is still discouraged in the php manual (but maybe they just did not update their manual on this, yet). A much bigger problem might be that a lot of common extensions are not thread-safe. So you might get threads with this php extension but the functions you're depending on are still not thread-safe so you will probably encounter race conditions, deadlocks and so on in code you did not write yourself...

来自 < strong > pthread 扩展的 PHP 手册:

Pthread 是一个面向对象的 API,它允许用户在 PHP 中进行多线程处理。它包括创建针对 Web 或控制台的多线程应用程序所需的所有工具。PHP 应用程序可以创建,读,写,执行和同步线程,工人和堆栈。

尽管这听起来难以置信,但这是完全真实的。今天,PHP 可以为那些希望尝试它的人提供多线程。

PHP 的第一个版本是2000年5月22日发布的 PHP4,它提供了一个线程安全的架构——一种在多线程 SAPI (Server API)环境中在单独的线程中执行多个解释器实例的方法。在过去的13年里,这个架构的设计一直保持并且进步: 从那时起,它已经在世界上最大的网站上投入生产使用。

用户领域中的线程从来都不是 PHP 团队所关心的问题,现在仍然如此。您应该了解,在 PHP 从事业务的世界中,已经有一种定义好的方法来扩展添加硬件。PHP 已经存在了很多年,硬件变得越来越便宜,因此 PHP 团队越来越不关心这个问题。虽然它变得越来越便宜,但它也变得更加强大; 今天,我们的移动电话和平板电脑有双核和四核架构,以及大量的内存,我们的台式机和服务器通常有8或16核,16和32GB 的内存,虽然我们可能不总是能够在预算内有两个,并有两台台式机很少对我们大多数人有用。

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

对于大多数人来说,多线程编程并不容易,即使使用最一致、最可靠的 API,也有许多不同的问题需要考虑,还有许多误解。PHP 小组不希望用户领域的多线程成为一个核心特性,它从来没有得到认真的关注-这是正确的。PHP 对每个人来说都不应该是复杂的。

考虑到所有这些因素,允许 PHP 利用其生产准备和测试功能,以便最大限度地利用我们所拥有的,当添加更多并不总是一个选项,并且对于许多任务并不是真正需要的时候,仍然有好处。

对于那些希望研究它的人来说,pthread 实现了一个允许用户使用多线程 PHP 应用程序的 API。它的 API 是一项正在进行的工作,并指定了一个稳定性和完整性的 beta 级别。

众所周知,PHP 使用的一些库不是线程安全的,程序员应该清楚,pthread 不能改变这一点,也不会尝试去改变。但是,任何线程安全的库都是可用的,就像解释器的任何其他线程安全设置一样。

Pthread 使用 Posix Threads (甚至在 Windows 中) ,程序员创建的是真正的执行线程,但是要让这些线程有用,它们必须了解 PHP ——能够执行用户代码、共享变量并允许有用的通信方式(同步)。因此,每个线程都是用解释器的一个实例创建的,但是根据设计,它的解释器与解释器的所有其他实例是隔离的——就像多线程服务器 API 环境一样。Pthread 试图以一种理智和安全的方式弥补这一差距。C 语言中线程程序员的许多顾虑对 pthread 程序员来说是不存在的,根据设计,pthread 是读复制和写复制(RAM 很便宜) ,所以不会有两个实例操作相同的物理数据,但是它们都可以影响另一个线程中的数据。PHP 可能在其核心编程中使用线程不安全特性的事实是完全无关的,用户线程和它的操作是完全安全的。

为什么要读写复制:

public function run() {
...
(1) $this->data = $data;
...
(2) $this->other = someOperation($this->data);
...
}


(3) echo preg_match($pattern, $replace, $thread->data);

(1)在 pthread 对象数据存储器上保持读写锁时,将数据从其原始存储器位置复制到对象存储器。Pthread 不会调整变量的重新计算,如果没有进一步的引用,Zend 可以释放原始数据。

(2) some Operation 的参数引用对象存储,原始数据存储,它本身是(1)结果的副本,再次为引擎复制到 zval 容器中,当发生这种情况时,在对象存储上持有读锁,锁被释放,引擎可以执行函数。当创建 zval 时,它的重新计数为0,使引擎能够在操作完成时释放副本,因为不存在对它的其他引用。

(3) preg _ match 的最后一个参数引用数据存储区,获得一个读锁,将(1)中的数据集复制到 zval 中,同样是0。锁被释放,对 preg _ match 的调用操作数据的副本,这本身就是原始数据的副本。

注意事项:

  • 线程安全地存储数据的对象存储区的哈希表是
    基于 PHP 附带的 TsHashTable,by Zend.

  • 对象存储有一个读写锁,为 TsHashTable 提供了一个额外的访问锁,如果需要(它确实需要,var _ dump/print _ r,当 PHP 引擎想要引用它们时,可以直接访问属性) pthread 可以在定义的 API 之外操作 TsHashTable。

  • 只有在进行复制操作时才会持有这些锁,在复制完成后,才会按照合理的顺序释放这些锁。

这意味着:

  • 当发生写操作时,不仅持有读写锁,而且持有 附加访问锁定。表本身是锁定的,没有 其他上下文锁定、读取、写入或影响它的可能方式

  • 当发生读取时,不仅持有读取锁,而且 额外的访问锁定,表又被锁定了。

没有两个上下文可以物理地或并发地从对象存储中访问相同的数据,但是在任何上下文中使用引用进行的写操作都会影响在任何上下文中使用引用读取的数据。

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

大多数可用于 PHP 的库和扩展都是围绕第三方的薄包装,PHP 核心功能在某种程度上是相同的。Pthread 不是 Posix Threads 的瘦包装器; 它是基于 Posix Threads 的线程 API。在 PHP 中实现用户不理解或不能使用的线程是没有意义的。一个不知道互斥对象是什么或者不知道互斥对象是什么的人没有理由不能利用他们所拥有的一切,包括技能和资源。对象的功能类似于对象,但是在两个上下文发生冲突的地方,pthread 提供了稳定性和安全性。

任何在 java 中工作过的人都会看到 pthread 对象和 java 中的线程之间的相似之处,这些人无疑会看到一个名为 ConcurrentModficationException 的错误——因为如果两个线程同时写入相同的物理数据,这听起来像是 java 运行时引发的错误。我理解它为什么存在,但是它让我困惑的是,资源如此便宜,再加上运行时能够在用户可以获得安全的准确且唯一的时间检测到并发,它选择在运行时抛出一个可能致命的错误,而不是管理数据的执行和访问。

我相信,pthread 不会发出这种愚蠢的错误,编写 API 是为了使线程尽可能稳定和兼容。

多线程不像使用一个新的数据库,应该密切关注手册中的每一个词和 pthread 附带的示例。

最后,来自 PHP 手册:

Pthread 过去是,现在也是一个有着非常好结果的实验。它的任何限制或特性都可能随时改变,这就是实验的本质。它的局限性(通常是由实现造成的)有充分的理由存在; pthread 的目的是为 PHP 中任何级别的多任务提供一个可用的解决方案。在 pthread 执行的环境中,为了提供一个稳定的环境,一些限制是必要的。

听说过技术部的 appserver吗?

它是用 php 编写的,作为 appserver 管理高流量 php 应用程序的多线程。还在测试阶段,但是很有前途。