<?php
$main = new MainLoop();
$main->start();
$file = new FileFetcher('news.txt'); // файл из дистра php 357 kb
$file->start();
$file->notify();
$file->wait();
while(!$file->feof){
$file->fetch();
echo 'Data fetched'."\n";
$file->notify();
$file->wait();
}
class MainLoop extends Thread
{
public function run()
{
while(true)
{
$time = microtime(true);
usleep(100000); // 99 ms // 0.1 sec
echo 'Time: '.(int)((microtime(true) - $time) * 1000)." ms\n";
}
}
}
class FileFetcher extends Thread
{
public function __construct($file)
{
$this->file = $file;
$this->fd = fopen($this->file, 'r');
}
public function run()
{
$this->feof = feof($this->fd);
while(!$this->feof){
$time = microtime(true);
$this->data = fread($this->fd, 1024);
sleep(1);
echo 'Data read time: '.(int)((microtime(true) - $time) * 1000)." ms\n";
$this->notify();
$this->wait();
$this->feof = feof($this->fd);
}
}
protected function fetch()
{
$data = $this->data;
$this->data = null;
return $data;
}
protected function feof()
{
return $this->feof;
}
}
Теперь о том, что я получил на выходе:
Data read time: 999 ms
Time: 99 ms
Data fetched
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Data read time: 999 ms
Time: 99 ms
Data fetched
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Data read time: 999 ms
Time: 99 ms
Data fetched
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Time: 99 ms
Data read time: 999 ms
Time: 99 ms
Data fetched
Оно работает!
Теперь главный вопрос нахрена это упало?
Представим у вас есть приложение демон (да вы извращенец пишите демонов на php)
И в вашем демоне есть полноценные сокеты и свой протокол, к примеру вы хотите запросить файл клиентом со своего сервера, но кто уже так делал знают, что при операциях с файлами используя fread, fgets и другие функции например dio (если вы работаете с крупными файлами и вам не хочется, забивать кеш ибо попадание в кеш при файловых операциях в вашем демоне стремится к нулю), основной поток замораживается и создается задержка. Даже если вести чтение мелкими блоками, что очень не эффективно, задержка уменьшается.
Поясняю есть у нас сокет который должен обрабатывать запросы пользователя как можно быстрее, при единичном потоке, наше приложение будет занимать поток при чтении файлов или обращении к базе и в этот самый коварный момент от клиента пришел запрос на сокете, но сокет не обработает его тут-же он будет ждать пока ему вновь будет передано управление, таким образом мы получим задержку.
В целом такая фигня важна малому количеству народа, мануала для php в инете почти нет, а было интересно проверить каким хитрым неблокирующим методом читать файл.
И тут у меня 3 потока в общей сложности.
1. Основной поток php
2. MainLoop
3. FileFetcher.
Смысл был в том, чтобы MainLoop не морозился и не было в нем задержек, а вот в потоке FileFetcher задержка может варьироваться в зависимости от нагрузки на файловую систему, так, что ничто не мешает нам с минимальной задержкой пользоваться MainLoop в котором может быть реализация сокетов.