467

是否有一种在 PHP 中实现多线程模型的现实方法,无论是真正的还是只是模拟它。前段时间有人建议您可以强制操作系统加载 PHP 可执行文件的另一个实例并处理其他同步进程。

这样做的问题是,当 PHP 代码完成执行 PHP 实例时,PHP 实例仍保留在内存中,因为无法从 PHP 中杀死它。因此,如果您正在模拟多个线程,您可以想象会发生什么。所以我仍在寻找一种可以在 PHP 中有效地完成或模拟多线程的方法。有任何想法吗?

4

15 回答 15

484

多线程在 php 中是可能的

是的,您可以使用pthreads在 PHP 中执行多线程

PHP 文档

pthreads 是一个面向对象的 API,它提供了 PHP 中多线程所需的所有工具。PHP 应用程序可以创建、读取、写入、执行和与线程、Worker 和线程对象同步。

警告:pthreads 扩展不能在 Web 服务器环境中使用。因此,PHP 中的线程应该只保留给基于 CLI 的应用程序。

简单测试

#!/usr/bin/php
<?php
class AsyncOperation extends Thread {

    public function __construct($arg) {
        $this->arg = $arg;
    }

    public function run() {
        if ($this->arg) {
            $sleep = mt_rand(1, 10);
            printf('%s: %s  -start -sleeps %d' . "\n", date("g:i:sa"), $this->arg, $sleep);
            sleep($sleep);
            printf('%s: %s  -finish' . "\n", date("g:i:sa"), $this->arg);
        }
    }
}

// Create a array
$stack = array();

//Initiate Multiple Thread
foreach ( range("A", "D") as $i ) {
    $stack[] = new AsyncOperation($i);
}

// Start The Threads
foreach ( $stack as $t ) {
    $t->start();
}

?>

第一次运行

12:00:06pm:     A  -start -sleeps 5
12:00:06pm:     B  -start -sleeps 3
12:00:06pm:     C  -start -sleeps 10
12:00:06pm:     D  -start -sleeps 2
12:00:08pm:     D  -finish
12:00:09pm:     B  -finish
12:00:11pm:     A  -finish
12:00:16pm:     C  -finish

第二轮

12:01:36pm:     A  -start -sleeps 6
12:01:36pm:     B  -start -sleeps 1
12:01:36pm:     C  -start -sleeps 2
12:01:36pm:     D  -start -sleeps 1
12:01:37pm:     B  -finish
12:01:37pm:     D  -finish
12:01:38pm:     C  -finish
12:01:42pm:     A  -finish

现实世界的例子

error_reporting(E_ALL);
class AsyncWebRequest extends Thread {
    public $url;
    public $data;

    public function __construct($url) {
        $this->url = $url;
    }

    public function run() {
        if (($url = $this->url)) {
            /*
             * If a large amount of data is being requested, you might want to
             * fsockopen and read using usleep in between reads
             */
            $this->data = file_get_contents($url);
        } else
            printf("Thread #%lu was not provided a URL\n", $this->getThreadId());
    }
}

$t = microtime(true);
$g = new AsyncWebRequest(sprintf("http://www.google.com/?q=%s", rand() * 10));
/* starting synchronization */
if ($g->start()) {
    printf("Request took %f seconds to start ", microtime(true) - $t);
    while ( $g->isRunning() ) {
        echo ".";
        usleep(100);
    }
    if ($g->join()) {
        printf(" and %f seconds to finish receiving %d bytes\n", microtime(true) - $t, strlen($g->data));
    } else
        printf(" and %f seconds to finish, request failed\n", microtime(true) - $t);
}
于 2013-03-19T13:55:02.653 回答
58

你为什么不使用popen

for ($i=0; $i<10; $i++) {
    // open ten processes
    for ($j = 0; $j < 10; $j++) {
        $pipe[$j] = popen('script2.php', 'w');
    }

    // wait for them to finish
    for ($j = 0; $j < 10; ++$j) {
        pclose($pipe[$j]);
    }
}
于 2010-12-03T22:27:03.310 回答
27

线程在 PHP 中不可用,但可以通过将 HTTP 请求用作异步调用来进行并发编程。

将 curl 的超时设置设置为 1 并为要相互关联的进程使用相同的 session_id,您可以与会话变量进行通信,如下面的示例所示。使用这种方法,您甚至可以关闭浏览器,并且服务器上仍然存在并发进程。

不要忘记像这样验证正确的会话 ID:

http://localhost/test/verifysession.php?sessionid=[正确的id]

启动进程.php

$request = "http://localhost/test/process1.php?sessionid=".$_REQUEST["PHPSESSID"];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);
echo $_REQUEST["PHPSESSID"];

进程1.php

set_time_limit(0);

if ($_REQUEST["sessionid"])
   session_id($_REQUEST["sessionid"]);

function checkclose()
{
   global $_SESSION;
   if ($_SESSION["closesession"])
   {
       unset($_SESSION["closesession"]);
       die();
   }
}

while(!$close)
{
   session_start();
   $_SESSION["test"] = rand();
   checkclose();
   session_write_close();
   sleep(5);
}

验证会话.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
var_dump($_SESSION);

关闭进程.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
$_SESSION["closesession"] = true;
var_dump($_SESSION);
于 2009-07-03T14:31:49.187 回答
17

虽然你不能线程,但你确实在 php.ini 中有一定程度的进程控制。这里有用的两个函数集是:

过程控制功能 http://www.php.net/manual/en/ref.pcntl.php

POSIX 函数 http://www.php.net/manual/en/ref.posix.php

您可以使用 pcntl_fork 分叉您的进程 - 返回子进程的 PID。然后您可以使用 posix_kill 来处理该 PID。

也就是说,如果你杀死一个父进程,应该向子进程发送一个信号,告诉它死亡。如果 php 本身无法识别这一点,您可以注册一个函数来管理它并使用 pcntl_signal 进行干净退出。

于 2008-09-16T10:30:11.473 回答
13

pthreads PECL 扩展使使用线程成为可能

http://www.php.net/manual/en/book.pthreads.php

于 2013-10-31T18:07:50.777 回答
12

我知道这是一个老问题,但是对于搜索的人来说,现在有一个用 C 编写的 PECL 扩展,它提供了 PHP 多线程功能,它位于这里https://github.com/krakjoe/pthreads

于 2012-09-19T02:39:12.057 回答
9

您可以使用 exec() 运行命令行脚本(例如命令行 php),如果将输出通过管道传输到文件,那么您的脚本将不会等待命令完成。

我不太记得 php CLI 语法,但你会想要类似的东西:

exec("/path/to/php -f '/path/to/file.php' | '/path/to/output.txt'");

我认为出于安全原因,很多共享托管服务器默认禁用 exec(),但可能值得一试。

于 2008-09-16T14:03:11.103 回答
6

您可以选择:

  1. 多卷曲
  2. 可以使用相同的系统命令
  3. 理想的场景是,用 C 语言创建一个线程函数并在 PHP 中编译/配置。现在该函数将成为 PHP 的函数。
于 2011-05-10T07:40:26.270 回答
6

pcntl_fork 怎么样?

查看我们的手册页以获取示例:PHP pcntl_fork

<?php

    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        pcntl_wait($status); //Protect against Zombie children
    } else {
        // we are the child
    }

?>
于 2012-01-13T00:55:28.300 回答
6

如果您使用的是 Linux 服务器,则可以使用

exec("nohup $php_path path/script.php > /dev/null 2>/dev/null &")

如果你需要传递一些参数

exec("nohup $php_path path/script.php $args > /dev/null 2>/dev/null &")

在 script.php 中

$args = $argv[1];

或者使用 Symfony https://symfony.com/doc/current/components/process.html

$process = Process::fromShellCommandline("php ".base_path('script.php'));
$process->setTimeout(0);     
$process->disableOutput();     
$process->start();
于 2020-04-29T15:12:25.620 回答
5

根据您要执行的操作,您还可以使用 curl_multi 来实现它。

于 2011-01-25T04:45:09.980 回答
5

您可以模拟线程。PHP 可以通过 popen(或 proc_open)运行后台进程。这些进程可以通过标准输入和标准输出进行通信。当然,这些进程本身可以是一个 php 程序。这可能与您将得到的一样接近。

于 2010-08-26T21:33:57.870 回答
4

pcntl_fork如果打开了安全模式,它将无法在 Web 服务器环境中工作。在这种情况下,它只能在 PHP 的 CLI 版本中工作。

于 2012-02-02T04:07:04.687 回答
-2

在撰写我当前的评论时,我不知道 PHP 线程。我自己来这里寻找答案,但一种解决方法是,从 Web 服务器接收请求的 PHP 程序将整个答案公式委托给控制台应用程序,该控制台应用程序将其输出、请求的答案存储到二进制文件并且启动控制台应用程序的 PHP 程序会逐字节地返回该二进制文件作为对接收到的请求的回答。控制台应用程序可以用任何在服务器上运行的编程语言编写,包括那些具有适当线程支持的语言,包括使用 OpenMP 的 C++ 程序。

一个不可靠的、肮脏的技巧是使用 PHP 来执行控制台应用程序,“uname”,

uname -a

并将该控制台命令的输出打印到 HTML 输出,以找出服务器软件的确切版本。然后将完全相同版本的软件安装到 VirtualBox 实例,编译/组装任何完全独立的、最好是静态的二进制文件,然后将它们上传到服务器。从那时起,PHP 应用程序可以将这些二进制文件用作具有适当多线程的控制台应用程序的角色。当服务器管理员尚未将所有需要的编程语言实现安装到服务器时,这是一种肮脏、不可靠的解决方法。需要注意的是,在 PHP 应用程序接收到控制台应用程序的每个请求时,都会终止/退出/get_killed。

至于托管服务管理员对这种服务器使用模式的看法,我想这归结为文化。在北欧,服务提供商必须提供广告内容,如果允许执行控制台命令并允许上传非恶意软件文件,服务提供商有权在几分钟甚至 30 秒后终止任何服务器进程,那么托管服务管理员缺乏任何论据来形成适当的投诉。在美国和西欧,情况/文化非常不同,我相信在美国和/或西欧,托管服务提供商很有可能会拒绝为使用上述技巧的托管服务客户提供服务。这只是我的猜测,鉴于我在美国的个人经历 托管服务,并考虑到我从其他人那里听到的有关西欧托管服务的信息。在撰写我当前的评论(2018_09_01)时,我对南欧托管服务提供商、南欧网络管理员的文化规范一无所知。

于 2018-09-01T05:04:10.823 回答
-3

多线程意味着同时执行多个任务或进程,我们可以使用以下代码在php中实现这一点,虽然没有直接的方法在php中实现多线程,但我们可以通过以下方式实现几乎相同的结果。

chdir(dirname(__FILE__));  //if you want to run this file as cron job
 for ($i = 0; $i < 2; $i += 1){
 exec("php test_1.php $i > test.txt &");
 //this will execute test_1.php and will leave this process executing in the background and will go         

 //to next iteration of the loop immediately without waiting the completion of the script in the   

 //test_1.php , $i  is passed as argument .

}

测试_1.php

$conn=mysql_connect($host,$user,$pass);
$db=mysql_select_db($db);
$i = $argv[1];  //this is the argument passed from index.php file
for($j = 0;$j<5000; $j ++)
{
mysql_query("insert  into  test   set

                id='$i',

                comment='test',

                datetime=NOW() ");

}

这将同时执行两次test_1.php,两个进程将同时在后台运行,这样就可以在php中实现多线程。

这家伙在 php 中的多线程工作做得非常好

于 2013-11-05T12:51:26.120 回答