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

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


当前回答

取决于你要做什么,你也可以使用curl_multi来实现它。

其他回答

popen()/proc_open()即使在Windows中也可以并行工作。

最常见的陷阱是“fread/stream_get_contents”没有while循环。一旦你试图从正在运行的进程中fread(),它将阻塞在它之后运行的进程的输出(因为fread()等待至少一个字节到达)

添加stream_select()。最接近的类比是“foreach with timeout but for streams”,你传递几个数组来读写,每次调用stream_select()都会选择一个或多个流。函数通过引用更新原始数组,所以不要忘记在下次调用之前将其恢复到所有流。函数给它们一些时间来读或写。如果没有内容控制返回,允许我们重试循环。

// sleep.php
set_error_handler(function ($severity, $error, $file, $line) {
    throw new ErrorException($error, -1, $severity, $file, $line);
});

$sleep = $argv[ 1 ];

sleep($sleep);

echo $sleep . PHP_EOL;

exit(0);
// run.php
<?php

$procs = [];
$pipes = [];

$cmd = 'php %cd%/sleep.php';

$desc = [
    0 => [ 'pipe', 'r' ],
    1 => [ 'pipe', 'w' ],
    2 => [ 'pipe', 'a' ],
];

for ( $i = 0; $i < 10; $i++ ) {
    $iCmd = $cmd . ' ' . ( 10 - $i ); // add SLEEP argument to each command 10, 9, ... etc.

    $proc = proc_open($iCmd, $desc, $pipes[ $i ], __DIR__);

    $procs[ $i ] = $proc;
}

$stdins = array_column($pipes, 0);
$stdouts = array_column($pipes, 1);
$stderrs = array_column($pipes, 2);

while ( $procs ) {
    foreach ( $procs as $i => $proc ) {
        // @gzhegow > [OR] you can output while script is running (if child never finishes)
        $read = [ $stdins[ $i ] ];
        $write = [ $stdouts[ $i ], $stderrs[ $i ] ];
        $except = [];
        if (stream_select($read, $write, $except, $seconds = 0, $microseconds = 1000)) {
            foreach ( $write as $stream ) {
                echo stream_get_contents($stream);
            }
        }

        $status = proc_get_status($proc);

        if (false === $status[ 'running' ]) {
            $status = proc_close($proc);
            unset($procs[ $i ]);

            echo 'STATUS: ' . $status . PHP_EOL;
        }

        // @gzhegow > [OR] you can output once command finishes
        // $status = proc_get_status($proc);
        //
        // if (false === $status[ 'running' ]) {
        //     if ($content = stream_get_contents($stderrs[ $i ])) {
        //         echo '[ERROR]' . $content . PHP_EOL;
        //     }
        //
        //     echo stream_get_contents($stdouts[ $i ]) . PHP_EOL;
        //
        //     $status = proc_close($proc);
        //     unset($procs[ $i ]);
        //
        //     echo 'STATUS: ' . $status . PHP_EOL;
        // }
    }

    usleep(1); // give your computer one tick to decide what thread should be used
}

// ensure you receive 1,2,3... but you've just run it 10,9,8...

exit(0);

取决于你要做什么,你也可以使用curl_multi来实现它。

As of the writing of my current comment, I don't know about the PHP threads. I came to look for the answer here myself, but one workaround is that the PHP program that receives the request from the web server delegates the whole answer formulation to a console application that stores its output, the answer to the request, to a binary file and the PHP program that launched the console application returns that binary file byte-by-byte as the answer to the received request. The console application can be written in any programming language that runs on the server, including those that have proper threading support, including C++ programs that use OpenMP.

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

uname -a

and print the output of that console command to the HTML output to find out the exact version of the server software. Then install the exact same version of the software to a VirtualBox instance, compile/assemble whatever fully self-contained, preferably static, binaries that one wants and then upload those to the server. From that point onwards the PHP application can use those binaries in the role of the console application that has proper multi-threading. It's a dirty, unreliable, workaround to a situation, when the server administrator has not installed all needed programming language implementations to the server. The thing to watch out for is that at every request that the PHP application receives the console application(s) terminates/exit/get_killed.

As to what the hosting service administrators think of such server usage patterns, I guess it boils down to culture. In Northern Europe the service provider HAS TO DELIVER WHAT WAS ADVERTISED and if execution of console commands was allowed and uploading of non-malware files was allowed and the service provider has a right to kill any server process after a few minutes or even after 30 seconds, then the hosting service administrators lack any arguments for forming a proper complaint. In United States and Western Europe the situation/culture is very different and I believe that there's a great chance that in U.S. and/or Western Europe the hosting service provider will refuse to serve hosting service clients that use the above described trick. That's just my guess, given my personal experience with U.S. hosting services and given what I have heard from others about Western European hosting services. As of the writing of my current comment(2018_09_01) I do not know anything about the cultural norms of the Southern-European hosting service providers, Southern-European network administrators.

如果您正在使用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();

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

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