Как можно использовать многопоточность в приложениях PHP - PullRequest
365 голосов
/ 16 сентября 2008

Существует ли реалистичный способ реализации многопоточной модели в PHP, будь то по-настоящему или просто имитировать ее. Некоторое время назад было предложено заставить операционную систему загружать другой экземпляр исполняемого файла PHP и обрабатывать другие одновременные процессы.

Проблема в том, что когда PHP-код завершает выполнение экземпляра PHP, он остается в памяти, потому что нет способа уничтожить его изнутри PHP. Поэтому, если вы моделируете несколько потоков, вы можете представить, что произойдет. Поэтому я все еще ищу способ, которым многопоточность может быть эффективно реализована или смоделирована изнутри PHP. Есть идеи?

Ответы [ 17 ]

390 голосов
/ 19 марта 2013

Многопоточность возможна в php

Да, вы можете выполнять многопоточность в PHP с pthreads

Из документации PHP :

pthreads - это объектно-ориентированный API, который предоставляет все инструменты, необходимые для многопоточности в PHP. Приложения PHP могут создавать, читать, писать, выполнять и синхронизировать с объектами Threads, Workers и Threaded.

Внимание : Расширение 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);
}
34 голосов
/ 04 декабря 2010

почему бы вам не использовать 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]);
    }
}
18 голосов
/ 03 июля 2009

Потоковая передача недоступна на складе PHP, но параллельное программирование возможно при использовании HTTP-запросов в качестве асинхронных вызовов.

Если для параметра curl для тайм-аута установлено значение 1 и используется один и тот же session_id для процессов, которые вы хотите связать друг с другом, вы можете связываться с переменными сеанса, как в моем примере ниже. С помощью этого метода вы можете даже закрыть браузер, и параллельный процесс все еще существует на сервере.

Не забудьте проверить правильный идентификатор сессии, как это:

http://localhost/test/verifysession.php?sessionid=[the правильный идентификатор]

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);
11 голосов
/ 16 сентября 2008

Несмотря на то, что вы не можете создавать потоки, у вас есть некоторая степень контроля над процессом в 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.

8 голосов
/ 31 октября 2013

использование потоков стало возможным благодаря расширению pthreads PECL

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

8 голосов
/ 19 сентября 2012

Я знаю, что это старый вопрос, но для людей, которые ищут, есть расширение PECL, написанное на C, которое теперь дает возможность многопоточности PHP, оно находится здесь https://github.com/krakjoe/pthreads

5 голосов
/ 16 сентября 2008

Вы можете использовать exec () для запуска сценария командной строки (например, php командной строки), и если вы передадите вывод в файл, ваш сценарий не будет ждать завершения команды.

Я не совсем помню синтаксис php CLI, но вы бы хотели что-то вроде:

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

Я думаю, что для некоторых серверов общего хостинга exec () отключен по умолчанию из соображений безопасности, но, возможно, стоит попробовать.

5 голосов
/ 27 августа 2010

Вы можете смоделировать потоки. PHP может запускать фоновые процессы через popen (или proc_open). Эти процессы могут быть переданы через stdin и stdout. Конечно, эти процессы сами могут быть php-программами. Это, вероятно, так близко, как вы получите.

3 голосов
/ 23 февраля 2011

Я знаю, что это старый, но вы могли бы посмотреть на http://phpthreadlib.sourceforge.net/

Он поддерживает двунаправленную связь между потоками, а также имеет встроенную защиту для уничтожения дочерних потоков (предотвращения сирот).

3 голосов
/ 25 января 2011

В зависимости от того, что вы пытаетесь сделать, вы также можете использовать curl_multi для достижения этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...