Многопоточность в PHP


Существует ли в PHP многопоточность?

PHP относится к языкам, в которых поддержка многопоточности отсутствует. Но, есть немало задач, в которых она была бы очень полезна. В большинстве случаев вам не нужно порождать и создавать новых потоков вообще и можно получить отличную производительность и без этого.

Сокеты

Очень полезная штука, когда вам надо дёргать чужие страницы или просто посылать запросы к другим сайтам.

Недавно поступил заказ на скрипт, который будет дёргать информацию с одного сайта, сохранять в базу, а потом переносить на другой. Порой скрипт делает запросы к 100+ страницам. Если этот скрипт бы выполнялся последовательно, то заняло бы много времени.

В этом деле поможет функция stream_socket_client

Прелесть в том, что, создавая запросы к сайтам этой функцией в асинхронном режиме, нам не придётся ждать ответа. Задача состоит из двух частей.

ПОЛЕЗНО  Удаление пустых строк

Создать сокеты
$flag = STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_CONNECT;
$sockets = array();
$id = 0;
$timeout = 60;
$convenient_read_block = 8192;
$result = array();
$url = array();
for ($i = $this - > beg_ID; $i <= $this - > end_ID; ++$i) {
    $fp = stream_socket_client('tcp://'.$host.
        ':'.$this - > port, $err, $errstr, 8, $flag);
    if (!$fp) {
        echo 'httpPost error: '.$errstr;
        return NULL;
    } else {
        stream_set_blocking($fp, 0);
        $sockets[$id++] = $fp;
        $url[$id - 1] = $i;
    }
}

Как видно, создаются сокеты и ссылки на них сохраняются в массиве $sockets. Теперь необходимо организовать прослушку ответов.

Ждём ответа

В некоторых источниках видел исходники, где функция на запись в сокет вызывается сразу после создания(применяли к $fp). На самом деле необходимо организовать прослушку на готовность сокета и к чтению, и к записи.

//создаём массивы сокетов для прослушки
$toRead = $toWrite = $sockets;
	//пока есть сокеты, чтение с которых мы ждём
	while (count($toRead) > 0) {
		$read=$toRead;
		$write=$toWrite;
		stream_select($read, $write, $e=null, $timeout);
			//есть сокеты, готовые к записи
			if (count($write)) {
				foreach ($write as $w) {
					$id=array_search($w, $sockets);
					//$query="GET /product.php?pid=PD".$id." HTTP/1.0\r\n". 
					$query="GET /product.php?pid=PD0".$url[$id]." HTTP/1.0\r\n". "Host: ".$host."\r\n". "User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1\r\n". "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*\/*;q=0.8". "Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3\r\n". "Accept-Encoding: gzip, deflate\r\n". "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7\r\n". "Connection: close\r\n\r\n";
					fwrite($w, $query);
					unset($toWrite[array_search($w, $toWrite)]);
				}
			}
		//есть сокеты, готовые к чтению
		if (count($read)) {
			foreach ($read as $r) {
				$id=array_search($r, $sockets);
				
				if(!isset($result[$id])) $result[$id]='';
				$data=fread($r, $convenient_read_block); 
				
				if (strlen($data) == 0) {
					fclose($r);
					unset($toRead[array_search($r, $toRead)]); 
				} else {
					$result[$id] .= $data;
				}
			}
		}
	}

Мы используем функцию stream_select() для ожидания возникновения событий на открытых сокетах. Вы можете ожидать готовности чтения, записи или исключительных событий (параметр первый, второй и третий соответственно). stream_select() будет ждать $timeout секунд пока событие не появится – когда же это случится, функция будет модифицировать массивы, которые Вы ей передали, так что они будут содержать идентификаторы сокетов, удовлетворяющих Вашему критерию.

ПОЛЕЗНО  Самый простой способ очистки массива от пустых элементов PHP

Ну, как примечание, скажу, что доступна эта прелесть с PHP 5.

источник