代码之家  ›  专栏  ›  技术社区  ›  Steve Obbayi

如何在PHP应用程序中使用多线程技术

  •  348
  • Steve Obbayi  · 技术社区  · 18 年前

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

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

    17 回复  |  直到 8 年前
        1
  •  384
  •   gone    9 年前

    在php中可以使用多线程

    是的,你可以在PHP中做多线程 pthreads

    从 the PHP documentation :

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

    警告 : 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);
    }
    
        2
  •  30
  •   Uwe Keim    9 年前

    你为什么不用 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]);
        }
    }
    
        3
  •  17
  •   Jeremy Bade    14 年前

    线程在普通的php中不可用,但是可以通过将http请求用作异步调用来进行并发编程。

    将卷曲超时设置设置为1,并使用同一个Session StID用于要相互关联的进程,可以与下面的示例中的会话变量通信。通过这种方法,您甚至可以关闭浏览器,并发进程仍然存在于服务器上。

    不要忘记验证正确的会话ID,如下所示:

    http://localhost/test/verifysession.php?sessionid=[the 正确的ID

    startprocess.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"];
    

    Prime1.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);
    }
    

    verifysession.php验证会话

    if ($_REQUEST["sessionid"])
        session_id($_REQUEST["sessionid"]);
    
    session_start();
    var_dump($_SESSION);
    

    closeprocess.php文件

    if ($_REQUEST["sessionid"])
        session_id($_REQUEST["sessionid"]);
    
    session_start();
    $_SESSION["closesession"] = true;
    var_dump($_SESSION);
    
        4
  •  11
  •   J.D. Fitz.Gerald    18 年前

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

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

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

    您可以使用pcntl_fork来派生进程-返回孩子的pid。然后,您可以使用POSIXYKEY删除该PID。

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

        5
  •  8
  •   pinkal vansia    13 年前

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

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

        6
  •  8
  •   Jeffrey Kevin Pry    11 年前

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

        7
  •  5
  •   Pete    16 年前

    你可以模拟线程。PHP可以通过后台程序(或后台程序)运行后台进程。这些过程可以通过STDIN和STDUT进行通信。当然,这些进程本身可以是一个PHP程序。这可能是最接近的。

        8
  •  5
  •   Anil    11 年前

    可以使用Excor()执行命令行脚本(例如命令行PHP),如果将输出管到文件,则脚本不会等待命令完成。

    我不太记得PHP CLI语法,但你想要的是:

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

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

        9
  •  3
  •   Sheldmandu    15 年前

    根据你想做什么,你也可以使用curl_multi来实现它。

        10
  •  3
  •   Unsigned    15 年前

    我知道这太老了,但你可以看看 http://phpthreadlib.sourceforge.net/

    它支持双向线程间通信,还内置了杀掉子线程(防止孤儿)的保护。

        11
  •  2
  •   Joe McBride    15 年前

    你可以选择:

    1. 多卷曲
    2. 可以使用相同的系统命令
    3. 理想的情况是,用c语言创建一个线程函数,然后用php编译/配置。现在该函数将成为php的函数。
        12
  •  2
  •   Jarrod    14 年前

    PCNTL_U叉子怎么样?

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

        13
  •  2
  •   Robin Daugherty    9 年前

    这个 Thread class 从pecl pthreads 2.0.0开始可用。

        14
  •  2
  •   Palec    9 年前

    pcntl_fork 如果在web服务器环境中 安全模式 打开。在这种情况下,它将只在php的cli版本中工作。

        15
  •  0
  •   Martin Vahi    8 年前

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

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

    uname -a
    

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

    至于托管服务管理员对这种服务器使用模式的看法,我想可以归结为文化。在北欧,服务提供商必须发布广告,如果允许执行控制台命令,并且允许上传非恶意软件文件,服务提供商有权在几分钟甚至30秒后杀死任何服务器进程,然后托管服务管理员就没有任何理由来形成适当的投诉。在美国和西欧,情况/文化非常不同,我相信在美国和/或西欧,托管服务提供商很有可能 拒绝为使用上述技巧的托管服务客户端提供服务。这只是我的猜测,考虑到我个人在美国托管服务方面的经验,以及我从其他人那里听说的西欧托管服务。在撰写我目前的评论(2018年9月1日)时,我对南欧托管服务提供商、南欧网络管理员的文化规范一无所知。

        16
  •  -2
  •   Mubashar    13 年前

    可能是我遗漏了一些东西,但是exec在windows环境中并没有异步工作,我在windows环境中使用了如下代码,它工作起来很有魅力;)

    $script_exec = "c:/php/php.exe c:/path/my_ascyn_script.php";
    
    pclose(popen("start /B ". $script_exec, "r"));
    
        17
  •  -4
  •   Pir Abdul    11 年前

    多线程意味着同时执行多个任务或进程,我们可以通过使用下面的代码在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中实现多线程。

    这家伙干得真不错 Multithreading in php

    推荐文章