PHP 애플리케이션에서 멀티 스레딩을 사용하는 방법


414

PHP에서 멀티 스레드 모델을 실제로 구현하거나 시뮬레이션하는 방법으로 구현하는 현실적인 방법이 있습니까? 얼마 전에 운영 체제가 다른 PHP 실행 파일 인스턴스를로드하고 다른 동시 프로세스를 처리하도록 강제 할 수 있다는 제안이있었습니다.

이 문제는 PHP 코드 실행이 완료되면 PHP 인스턴스에서 PHP 인스턴스를 종료 할 수있는 방법이 없기 때문에 PHP 인스턴스 실행이 메모리에 남아 있다는 것입니다. 따라서 여러 스레드를 시뮬레이트하는 경우 어떻게 될지 상상할 수 있습니다. 그래서 여전히 PHP 내에서 멀티 스레딩을 효과적으로 수행하거나 시뮬레이션 할 수있는 방법을 찾고 있습니다. 어떤 아이디어?


1
여기에 내 질문과 답변을 참조하십시오 stackoverflow.com/questions/2101640/...
powtac



아마 관심있을만한
것들

이제 2020 년에 "병렬" php.net/manual/en/intro.parallel.php 가 "pthreads"대신 원하는 것 같습니다 : stackoverflow.com/a/56451969/470749
Ryan

답변:


431

PHP에서 멀티 스레딩 가능

예, pthreads를 사용 하여 PHP에서 멀티 스레딩을 수행 할 수 있습니다

에서 PHP를 문서 :

pthreads는 PHP에서 멀티 스레딩에 필요한 모든 도구를 제공하는 객체 지향 API입니다. PHP 응용 프로그램은 스레드, 작업자 및 스레드 개체를 생성, 읽기, 쓰기, 실행 및 동기화 할 수 있습니다.

경고 : pthreads 확장은 웹 서버 환경에서 사용할 수 없습니다. 따라서 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);
}

3
@Baba, Xampp 서버에서 pthread를 구성하고 설치할 수 없습니다. 저 좀 도와 주실 래요?
Irfan

4
windows 바이너리 다운로드 here windows.php.net/downloads/pecl/releases/pthreads/0.0.45
Baba

17
다행입니다. PHP를 몇 년 동안 만지지 않았으며 이제 멀티 스레딩 기능이 있습니다!
크루저

1
좋고 간단합니다! 참고로 Azure Cloud Win 서버에 앱을 배포하고 있으며 기본 1 코어 구성 만 선택하면 더 많은 코어를 추가하지 않으면 멀티 스레딩을 사용할 수 없습니다.
밀라노

3
주의 사항 : 조 왓킨스, 새로운 병렬 확장에 찬성 개발을 중단 pthreads의 확장의 저자 : github.com/krakjoe/pthreads/issues/929
안톤 Belonovich

42

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]);
    }
}

4
위의 솔루션을 사용하고 있으며 잘 작동합니다 .php를 사용하여 병렬 프로세스를 수행하는 가장 쉬운 방법이라고 생각합니다.
GodFather

7
@ e-info128과 같이이 구현은 프로세스를 분기하므로 다른 프로세스에서 실행 중이며 프로세스 리소스를 공유하지 않습니다. 현재 진행중인 작업이 리소스를 공유 할 필요가없는 경우에도 여전히 작동하며 병렬로 실행됩니다.
Raffi

1
세션 변수를 사용하지 않고 변수를 popen에 어떻게 전달합니까?
atwellpub 2015 년

@atwellpub 절대 아닙니다. 리소스를 공유하지 않는 별도의 프로세스입니다. 세션조차도 어색한 IPC 메커니즘이 될 것입니다
Cholthi Paul Ttiopic

1
그들에게 데이터를 전달하기 위해 인수와 Redis 서버도 사용할 수 있습니다.
Amir Fo

21

스레딩은 기본 PHP에서 사용할 수 없지만 HTTP 요청을 비동기 호출로 사용하여 동시 프로그래밍이 가능합니다.

curl의 시간 초과 설정을 1로 설정하고 서로 연결하려는 프로세스에 동일한 session_id를 사용하면 아래 예와 같이 세션 변수와 통신 할 수 있습니다. 이 방법을 사용하면 브라우저를 닫을 수 있으며 동시 프로세스는 여전히 서버에 존재합니다.

다음과 같이 올바른 세션 ID를 확인하는 것을 잊지 마십시오 :

http : //localhost/test/verifysession.php? sessionid = [ 올바른 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"];

process1.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
마지막으로 (몇 년 전) PHP는 두 프로세스가 동시에 파일 기반 세션 저장소에 액세스 할 수 없었습니다. 파일을 잠그고 두 번째 프로세스는 첫 번째 스크립트가 중지 될 때까지 대기해야합니다. CLI가 아닌 웹 서버 환경에 대해 이야기하고 있습니다.
Alexei Tenitski

5
set_time_limit(0);이케 절대 이렇게하지 마십시오.
Kafoso

@Kafoso Kafoso 왜 안돼? 글쎄, PHP를 웹 스크립트 프로세서로 동의하지만 CLI에서 왜 그렇지 않습니까? 문제가 발생하면 Ctrl + C로 CLI를 종료 할 수 있습니다.
sijanec

14

스레드 할 수는 없지만 PHP에는 어느 정도 프로세스 제어가 있습니다. 여기서 유용한 두 가지 기능 세트는 다음과 같습니다.

공정 제어 기능 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을 사용하여 완전히 종료 할 수 있습니다.


1
그 대답은 이제 매우 구식입니다 (11 살이라는 것을 아는 것은 매우 공정합니다). 아래의 pthread를보십시오.
Maciej Paprocki

@MaciejPaprocki pThread는 이제 PHP 7.4에서 중단되었습니다. 대신 병렬을 사용하십시오
Airy


10

나는 이것이 오래된 질문이라는 것을 알고 있지만 검색하는 사람들을 위해 지금 PHP 멀티 스레딩 기능을 제공하는 C로 작성된 PECL 확장이 있으며 여기에 있습니다.


pThread는 이제 PHP 7.4에서 중단되었습니다. 대신 병렬을 사용하십시오
Airy

5

exec ()를 사용하여 명령 행 스크립트 (예 : 명령 행 php)를 실행할 수 있으며 출력을 파일로 파이프하면 명령이 완료 될 때까지 스크립트가 대기하지 않습니다.

PHP CLI 구문을 기억할 수는 없지만 다음과 같은 것을 원할 것입니다.

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

꽤 많은 공유 호스팅 서버가 보안상의 이유로 기본적으로 exec ()가 비활성화되어 있다고 생각하지만 시도해 볼 가치가 있습니다.


4

스레딩을 시뮬레이션 할 수 있습니다. PHP는 popen (또는 proc_open)을 통해 백그라운드 프로세스를 실행할 수 있습니다. 이러한 프로세스는 stdin 및 stdout을 통해 통신 할 수 있습니다. 물론 이러한 프로세스 자체는 PHP 프로그램 일 수 있습니다. 아마 당신이 얻을 수있는 한 가깝습니다.


3

당신이하려는 일에 따라 curl_multi를 사용하여 달성 할 수도 있습니다.



3

다음과 같은 옵션이 있습니다.

  1. multi_curl
  2. 하나는 같은 시스템 명령을 사용할 수 있습니다
  3. 이상적인 시나리오는 C 언어로 스레딩 함수를 만들고 PHP로 컴파일 / 구성하는 것입니다. 이제 그 함수는 PHP의 함수가 될 것입니다.


3

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
    }

?>

2

pcntl_fork안전 모드 가 켜져 있으면 웹 서버 환경에서 작동하지 않습니다 . 이 경우, CLI 버전의 PHP에서만 작동합니다.


1

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();

-1

내 현재 의견을 쓸 때 PHP 스레드에 대해 모른다. 여기에서 답을 직접 찾아 보았지만 한 가지 해결 방법은 웹 서버에서 요청을받는 PHP 프로그램이 전체 답변 형식을 출력, 요청에 대한 응답, 이진 파일을 저장하는 콘솔 응용 프로그램에 위임한다는 것입니다 콘솔 응용 프로그램을 시작한 PHP 프로그램은 이진 파일을 바이트 단위로 수신 된 요청에 대한 응답으로 리턴합니다. 콘솔 응용 프로그램은 OpenMP를 사용하는 C ++ 프로그램을 포함하여 적절한 스레딩을 지원하는 언어를 포함하여 서버에서 실행되는 모든 프로그래밍 언어로 작성할 수 있습니다.

신뢰할 수없고 더럽고 트릭 인 방법 중 하나는 PHP를 사용하여 콘솔 응용 프로그램 "uname"을 실행하는 것입니다.

uname -a

해당 콘솔 명령의 출력을 HTML 출력으로 인쇄하여 서버 소프트웨어의 정확한 버전을 찾으십시오. 그런 다음 정확히 동일한 버전의 소프트웨어를 VirtualBox 인스턴스에 설치하고 원하는대로 완전히 포함 된 정적 바이너리를 컴파일 / 조립 한 다음 서버에 업로드하십시오. 그 시점부터 PHP 응용 프로그램은 적절한 멀티 스레딩이있는 콘솔 응용 프로그램의 역할에서 해당 바이너리를 사용할 수 있습니다. 서버 관리자가 서버에 필요한 프로그래밍 언어 구현을 모두 설치하지 않은 상황에 대한 더럽고 신뢰할 수없는 해결 방법입니다. 주의해야 할 것은 PHP 응용 프로그램이 콘솔 응용 프로그램을 수신 할 때마다 종료 / 종료 / get_killed를 요청한다는 것입니다.

호스팅 서비스 관리자가 그러한 서버 사용 패턴에 대해 어떻게 생각하는지에 대해서는 문화로 귀결됩니다. 북유럽에서 서비스 제공 업체는 광고 한 내용을 전달해야하며 콘솔 명령 실행이 허용되고 맬웨어가 아닌 파일의 업로드가 허용 된 경우 서비스 제공 업체는 몇 분 후 또는 30 초 후에 서버 프로세스를 종료 할 수 있습니다 호스팅 서비스 관리자에게는 적절한 불만 제기에 대한 주장이 없습니다. 미국과 서유럽의 상황 / 문화는 매우 다르며 미국 및 / 또는 서유럽에서는 호스팅 서비스 제공 업체가 위에서 설명한 트릭을 사용하는 호스팅 서비스 클라이언트의 서비스 제공을 거부 할 가능성이 크다고 생각합니다. 미국과의 개인적인 경험을 고려할 때 그것은 단지 내 추측입니다. 호스팅 서비스를 제공하고 서유럽 호스팅 서비스에 대해 다른 사람들로부터 들어 본 내용을 제공했습니다. 내 현재 의견을 쓸 때 (2018_09_01) 나는 남유럽 네트워크 서비스 관리자 인 남유럽 호스팅 서비스 제공 업체의 문화 규범에 대해 아무것도 모른다.


-3

내가 뭔가를 놓친 것일 수도 있지만 exec는 Windows에서 다음과 같이 Windows에서 사용하는 비동기식으로 작동하지 않았으며 매력처럼 작동했습니다.)

$script_exec = "c:/php/php.exe c:/path/my_ascyn_script.php";

pclose(popen("start /B ". $script_exec, "r"));

1
PHP에서 멀티 스레딩을 해킹하려면 여러 popen () 문을 열어야합니다 (PHP는 완료 될 때까지 기다리지 않습니다). 그런 다음 십여 개 정도 열린 후에는 pclose ()를 호출하십시오 (PHP는 pclose ()가 완료되기를 기다립니다). 코드에서 각 스레드를 연 직후에 스레드를 닫아 코드가 다중 스레드처럼 동작하지 않도록합니다.
Kmeixner 2016 년

-5

멀티 스레딩은 여러 작업이나 프로세스를 동시에 수행하는 것을 의미합니다. 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 .

}

Test_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에서 멀티 스레딩을 얻을 수 있습니다.

이 사람은 정말 좋은 일을 했습니다.


또한 이것은 멀티 스레딩과는 아무런 관련이 없습니다. 이것은 병렬 처리입니다. 완전히 다른 것들.
Digital Human

해결 방법, 비상 해킹이라는 견해로는 제공된 솔루션의 아이디어가 매우 적합하지만 동시성과 하드웨어 기반의 차이가 있기 때문에 다른 사람들이 "진정한 멀티 스레딩"을 구성하는 것에 대해 화염 전쟁을 일으킬 수 있다고 생각합니다. :에서 설명한 바와 같이, 가공 평행 youtube.com/watch?v=cN_DpYBzKso
마틴 Vahi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.