gpt4 book ai didi

php - 在 PHP 中的子进程之间共享变量?

转载 作者:可可西里 更新时间:2023-11-01 00:35:37 25 4
gpt4 key购买 nike

我确信我正在尝试的非常简单,但我之前从未完全使用过多线程,所以我不确定从哪里开始。

我正在使用 PCNTL创建一个多线程的 PHP 应用程序。我希望做的是同时运行 3 个函数,我希望它们的返回值合并到一个数组中。所以从逻辑上讲,我需要在所有 child 之间共享一些变量,他们将结果附加到其中,或者仅在单个 child 和父级之间共享三个变量 - 然后父级可以稍后合并结果。

问题是 - 我不知道该怎么做。首先想到的是使用 shared memory ,但我觉得应该有一个更简单的方法。

此外,如果它有任何效果,那么 fork 进程的函数是一个公共(public)类方法。所以我的代码如下所示:

<?php
class multithreaded_search {
/* ... */
/* Constructors and such */
/* ... */
public function search( $string = '' ) {
$search_types = array( 'tag', 'substring', 'levenshtein' );
$pids = array();
foreach( $search_types as $type ) {
$pid = pcntl_fork();
$pids[$pid] = $type;
if( $pid == 0 ) { // child process
/* confusion */
$results = call_user_func( 'multithreaded_search::'.$type.'_search', $string );
/* What do we do with $results ? */
}
}
for( $i = 0; $i < count( $pids ); $i++ ) {
$pid = pcntl_wait();
/* $pids[$pid] tells me the type of search that just finished */
/* If we need to merge results in the parent, we can do it here */
}
/* Now all children have exited, so the search is complete */
return $results;
}
private function tag_search( $string ) {
/* perform one type of search */
return $results;
}
private function substring_search( $string ) {
/* perform one type of search */
return $results;
}
private function levenshtein_search( $string ) {
/* perform one type of search */
return $results;
}
}
?>

所以我需要使用 shmop_open在我打电话之前 pcntl_fork创建共享内存并将结果保存在那里,还是 children 共享类变量?或者他们只共享全局变量?我相信答案很简单……我只是不知道。

答案(对于任何发现此问题的人)

我有几年的经验,所以我会尝试传授一些知识。

首先,在您的应用程序中实现多处理时,有两个重要的区别需要理解:
  • 线程 流程 fork 进程
  • 共享内存 消息传递

  • 线程、进程、 fork 进程
  • 线程 :线程的开销非常低,因为它们与父进程在相同的进程空间中运行并共享父进程的内存地址。这意味着为了创建或销毁线程而调用的操作系统更少。如果您打算经常创建和销毁线程,则线程是“便宜”的替代方案。 PHP 没有对线程的 native 支持。但是,从 PHP 7.2 开始,有提供线程功能的 PHP 扩展(用 C 编写)。例如:pthreads
  • 进程 :进程的开销要大得多,因为操作系统必须为它分配内存,而且在像 PHP 这样的解释型语言的情况下,通常有一个完整的运行时必须在您自己的代码执行之前加载和处理。 PHP 确实通过 exec 支持生成进程(同步)或 proc_open (异步)
  • fork 进程 :一个 fork 的过程 split 了这两种方法之间的差异。一个单独的进程在当前进程的内存空间中运行。通过 PCNTL 也有对此的本地支持

  • 为工作选择合适的工具通常是问一个问题:“你多久会启动额外的线程/进程”?如果不是那么频繁(也许您每小时运行一个批处理作业并且该作业可以并行化),那么流程可能是更简单的解决方案。如果进入您的服务器的每个请求都需要某种形式的并行计算,并且您每秒收到 100 个请求,那么线程可能是可行的方法。

    共享内存、消息传递
  • 共享内存 :这是当允许多个线程或进程写入 RAM 的同一部分时。这样做的好处是非常快速且易于理解 - 它就像办公空间中的共享白板。任何人都可以读取或写入它。然而,它在管理并发方面有几个缺点。想象一下,如果两个进程在完全相同的时间写入内存中完全相同的位置,那么第三个进程会尝试读取结果。它会看到什么结果? PHP 通过 shmop 原生支持共享内存,但要正确使用它需要锁、信号量、监视器或其他复杂的系统工程过程
  • 消息传递 :这是自 70 年代以来实际上一直存在的“热门新事物”™。这个想法是,不是写入共享内存,而是写入自己的内存空间,然后告诉其他线程/进程“嘿,我有一条消息要给你”。 Go 编程语言有一个与此相关的著名格言:“不要通过共享内存进行通信,通过通信共享内存”。传递消息的方式有很多种,包括:写入文件、写入套接字、写入标准输出、写入共享内存等。

  • 一个基本的socket解决方案

    首先,我将尝试从 2012 年开始重新创建我的解决方案。@MarcB 将我指向 UNIX sockets .本页明确提及 fsockopen ,它打开一个套接字作为文件指针。它还在“另请参阅”部分包含指向 socket_connect 的链接。 ,这使您可以对套接字进行较低级别的控制。

    当时我可能花了很长时间研究这些 socket_*功能,直到我开始工作。现在我在谷歌上快速搜索 socket_create_pair并找到 this helpful link to get you started

    我重写了上面的代码,将结果写入 UNIX 套接字,并将结果读入父线程:
    <?php
    /*
    * I retained the same public API as my original StackOverflow question,
    * but instead of performing actual searches I simply return static data
    */

    class multithreaded_search {
    private $a, $b, $c;
    public function __construct($a, $b, $c) {
    $this->a = $a;
    $this->b = $b;
    $this->c = $c;
    }

    public function search( $string = '' ) {
    $search_types = array( 'tag', 'substring', 'levenshtein' );
    $pids = array();
    $threads = array();
    $sockets = array();
    foreach( $search_types as $type ) {
    /* Create a socket to write to later */
    $sockets[$type] = array();
    socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets[$type]);
    $pid = pcntl_fork();
    $pids[] = $pid;
    $threads[$pid] = $type;
    if( $pid == 0 ) { // child process
    /* no more confusion */
    $results = call_user_func( 'multithreaded_search::'.$type.'_search', $string );
    /* What do we do with $results ? Write them to a socket! */
    $data = serialize($results);
    socket_write($sockets[$type][0], str_pad($data, 1024), 1024);
    socket_close($sockets[$type][0]);
    exit();
    }
    }
    $results = [];
    for( $i = 0; $i < count( $pids ); $i++ ) {
    $pid = $pids[$i];
    $type = $threads[$pid];
    pcntl_waitpid($pid, $status);
    /* $threads[$pid] tells me the type of search that just finished */
    /* If we need to merge results in the parent, we can do it here */
    $one_result = unserialize(trim(socket_read($sockets[$type][1], 1024)));
    $results[] = $one_result;
    socket_close($sockets[$type][1]);
    }
    /* Now all children have exited, so the search is complete */
    return $results;
    }

    private function tag_search() {
    return $this->a;
    }

    private function substring_search() {
    return $this->b;
    }

    private function levenshtein_search() {
    return $this->c;
    }
    }

    $instance = new multithreaded_search(3, 5, 7);
    var_dump($instance->search());

    笔记

    此解决方案使用 fork 进程和通过本地(内存中)套接字传递的消息。根据您的用例和设置,这可能不是最佳解决方案。例如:
  • 如果您希望在多个单独的服务器之间拆分处理并将结果传递回中央服务器,则 create_socket_pair不会工作。在这种情况下,您需要创建一个套接字,将套接字绑定(bind)到地址和端口,然后调用 socket_listen等待子服务器的结果。此外,pcntl_fork不能在多服务器环境中工作,因为进程空间不能在不同机器之间共享
  • 如果您正在编写命令行应用程序并且更喜欢使用线程,那么您可以使用 pthreads或抽象 pthread 的第三方库
  • 如果你不喜欢钻研杂草,只想简单的多处理而不必担心实现细节,请查看类似 Amp/Parallel 的库。
  • 最佳答案

    fork 的 child 只要在任何地方写入,就会获得他们自己的内存空间的专用副本 - 这就是“写时复制”。虽然 shmop 确实提供了对公共(public)内存位置的访问,但实际的 PHP 变量和脚本中未定义的内容不会在子级之间共享。

    $x = 7;在一个 child 中的 $x 不会使其他 child 中的 $x 也变成 7。每个 child 都会有自己的专属 $x,完全独立于其他人的副本。

    关于php - 在 PHP 中的子进程之间共享变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8707339/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com