Pull to refresh

Еще одна реализация многопоточности на PHP

Reading time8 min
Views7K
Итак… Приступим.
В последнее время я встретил сразу 2 реализации многопоточности на Хабре. Немного подумав я решил написать и свой вариант.
Но так как у меня нет возможности использовать PCNTL библиотеку, то мне пришлось извратиться…

Задача: реализация многопоточности на PHP (threads)

Издержки: нет модуля PCNTL ( build w/o PCNTL )

Решение: чистый PHP + Unix ( pure PHP + Unix )

Как это было… И как это стало.

Файл application.php
Это сам класс, реализующий триды (потоки) и позволяющий этим потокам и родителю обмениваться переменными. Как это происходит расскажу попозже.
<?
class Application
{
    public $child = false;
    private $file;
    private $tmpdir;
    private $children = array();
    private $vars = array();
    
    public function __construct($argv,$argc)
    {
        $GET = array();
        $this->file = str_replace(' ','\ ',getcwd()).'/'.$argv[0];
        for($i=1;$i<$argc;$i++)
        {
            if(substr($argv[$i],0,1)=='-' and ( isset($argv[($i+1)]) and substr($argv[($i+1)],0,1)!=='-'))
            {
                $GET[substr($argv[$i],1)] = $argv[($i+1)];
                $i++;
            }
            else
            {
                $GET[substr($argv[$i],1)] = true;
            }
        }
        $this->vars = $GET;
        if(isset($this->vars['tmpdir']))
        {
            $this->child = $this->vars['pid'];
            $this->tmpdir = $this->vars['tmpdir'];
        }
        else
        {
            $this->tmpdir = '/tmp/'.md5($this->file . time());
            mkdir($this->tmpdir);
        }
        mkdir($this->tmpdir.'/vars');
    }
    public function __destruct()
    {
        if($this->child == false)
        {
            if(file_exists($this->tmpdir.'/vars/'))
            {
                $vars = scandir($this->tmpdir.'/vars/');
                foreach($vars as $var) if(strlen($var)>3)unlink($this->tmpdir.'/vars/'.$var);
                rmdir($this->tmpdir.'/vars/');
            }
            if(file_exists($this->tmpdir.'/threads/')) rmdir($this->tmpdir.'/threads/');
            rmdir($this->tmpdir);
        }
        else unlink($this->tmpdir.'/threads/'.$this->child.'.pid');
    }
    public function __get($var)
    {
        $val = false;
        if(file_exists($this->tmpdir.'/vars/'.md5($var)))
        {
        $val = file_get_contents($this->tmpdir.'/vars/'.md5($var));
        $val = unserialize($val);
        }
        return $val;
    }
    public function __set($var,$val)
    {
        $fp = fopen($this->tmpdir.'/vars/'.md5($var), "w");
        if (flock($fp, LOCK_EX))
        {
            fwrite($fp, serialize($val));
            flock($fp, LOCK_UN);
        }else $this->{$var} = $val;
        fclose($fp);    
    }
    public function startThreads($count=0)
    {
        mkdir($this->tmpdir.'/threads');
        for($i=1;$i<=$count;$i++)
        {
            $pid = $this->tmpdir.'/threads/'.$i.'.pid';
            $this->children[$i]['pid'] = popen("php {$this->file} -tmpdir {$this->tmpdir} -pid {$i} > {$pid}&",'r');
        }
    }
    public function childs()
    {
        $dd = 0;
        $darr = scandir($this->tmpdir.'/threads/');
        foreach($darr as $d) if($d!='.' and $d!='..') $dd++;
        return $dd;
    }
    public function write($args,$pid=false)
    {
        if(!is_array($args)) $args = array($args);
        $str = serialize($args);
        $f = fopen($this->tmpdir.'/'.$this->child.'.pid','w');
        fwrite($f,$str);
        fclose($f);
    }
}
$Application = new Application($argv,$argc);
?>



А это маленький тест, с помощью которого мы можем проверить все ли работает. =)
Файл threads.php
<?
    include "../application.php";
    if( $Application->child !== false )
    {
        // Child
        print 'Start at ' . date("H:i:s") . chr(10);
        print 'I\'m child #' . $Application->child . chr(10);
        sleep(rand(0,10));
        $Application->MyVar++;
        print 'Stop at ' . date("H:i:s") . chr(10);
    }
    else
    {
        // Parent
        $Application->MyVar = 0;
        
        print 'I\'m parent. ' . $Application->child . chr(10);
        $Application->startThreads(10);
        
        $cur_c = 0;
        $las_c = 10;
        
        while( ($cur_c = $Application->childs()) > 0)
        {
            if($cur_c != $las_c)
            {
                print 'There are ' . $cur_c . ' childs on fly.' . chr(10);
                print 'MyVar: ' . $Application->MyVar . chr(10).chr(10);
            }
            $las_c = $cur_c;
            sleep(1);
        }
    }
    print 'MyVar: ' . $Application->MyVar . chr(10).chr(10);
?>



ЗЫ Сильно не бейте, это, так сказать мой первый (почти) пост…
ЗЫ ЗЫ Это кросс-пост из моего блога (AlexSnet.ru)

UPD: По просьбе хабралюдей.

Где это использовать и где это использовать не стоит


Данное решение требовательно к ресурсам, так как запуск еще одного процесса ПХП — очень требовательное к ресурсам задание.
В случае, если Вы можете использовать консоль или запускать скрипт из Cron'a лучше использовать решение coldFlamecoldFlame (Многопроцессовые демоны на PHP). Но тут тоже есть подводный камень и имя ему — PCNTL.
PCNTL (Process Control) требует специальной сборки и не работает под Win-системами.

Мой вариант следует использовать только тогда, когда нет возможности использовать уже названный выше метод.
Tags:
Hubs:
+6
Comments16

Articles

Change theme settings