Created
October 15, 2015 23:01
-
-
Save anonymous/b1d6a389e254a2587541 to your computer and use it in GitHub Desktop.
Coroutine example usage in PHP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
class Task | |
{ | |
protected $taskId; | |
protected $coroutine; | |
protected $sendValue = null; | |
protected $beforeFirstYield = true; | |
public function __construct($taskId, \Generator $coroutine) | |
{ | |
$this->taskId = $taskId; | |
$this->coroutine = $coroutine; | |
} | |
public function getTaskId() | |
{ | |
return $this->taskId; | |
} | |
public function setSendValue($sendValue) | |
{ | |
$this->sendValue = $sendValue; | |
} | |
public function run() | |
{ | |
if (!$this->beforeFirstYield) { | |
$retval = $this->coroutine->send($this->sendValue); | |
$this->sendValue = null; | |
return $retval; | |
} | |
$this->beforeFirstYield = false; | |
$current = $this->coroutine->current(); | |
return $current; | |
} | |
public function isFinished() | |
{ | |
return !$this->coroutine->valid(); | |
} | |
} | |
class Scheduler | |
{ | |
protected $index = 0; | |
protected $taskMap = []; | |
protected $taskQueue; | |
protected $msgs = []; | |
public function __construct() | |
{ | |
$this->taskQueue = new \SplQueue(); | |
} | |
public function newTask(\Generator $coroutine, $msg = null) | |
{ | |
$taskId = ++$this->index; | |
$task = new \Task($taskId, $coroutine); | |
$this->schedule($task); | |
$this->taskMap[$taskId] = $task; | |
$this->msgs[$taskId] = $msg; | |
return $taskId; | |
} | |
public function schedule(Task $task) | |
{ | |
$this->taskQueue->enqueue($task); | |
} | |
public function run() | |
{ | |
while (!$this->taskQueue->isEmpty()) { | |
$task = $this->taskQueue->dequeue(); | |
$task->setSendValue($this->msgs[$task->getTaskId()]); | |
$task->run(); | |
if ($task->isFinished()) { | |
unset($this->taskMap[$task->getTaskId()]); | |
continue; | |
} | |
$this->schedule($task); | |
} | |
} | |
} | |
function task1() | |
{ | |
for ($i = 1; $i <= 10; ++$i) { | |
echo yield. ">iteration $i.\n"; | |
} | |
} | |
function task2() | |
{ | |
for ($i = 1; $i <= 9; ++$i) { | |
echo yield.">iteration $i.\n"; | |
} | |
} | |
function task3() | |
{ | |
for ($i = 1; $i <= 8; ++$i) { | |
echo yield.">iteration $i.\n"; | |
} | |
} | |
function task4() | |
{ | |
for ($i = 1; $i <= 7; ++$i) { | |
echo yield.">iteration $i.\n"; | |
} | |
} | |
function task5() | |
{ | |
for ($i = 1; $i <= 6; ++$i) { | |
echo yield.">iteration $i.\n"; | |
} | |
} | |
$task1 = task1(); | |
$task2 = task2(); | |
$task3 = task3(); | |
$task4 = task4(); | |
$task5 = task5(); | |
$scheduler = new Scheduler; | |
$scheduler->newTask($task1, 'TASK1'); | |
$scheduler->newTask($task2, 'TASK2'); | |
$scheduler->newTask($task3, 'TASK3'); | |
$scheduler->newTask($task5, 'TASK5'); | |
$scheduler->newTask($task4, 'TASK4'); | |
$scheduler->run(); | |
//exit; | |
//Coroutines | |
function coroutine1() | |
{ | |
for ($i=0; $i<9999; $i++) { | |
yield $i => yield; | |
} | |
} | |
function coroutine2() | |
{ | |
for ($i=0; $i<9999; $i++) { | |
yield 1; | |
yield 2; | |
} | |
} | |
$coroutine1 = coroutine1(); | |
$coroutine2 = coroutine2(); | |
echo "coroutine1 -- example \n"; | |
$coroutine1->send('foo'); //its returns 'foo' | |
var_dump( | |
[ | |
'valid' => $coroutine1->valid(), | |
'key' => $coroutine1->key(), | |
'value' => $coroutine1->current(), | |
] | |
); | |
echo "coroutine2 -- example \n"; | |
var_dump( | |
[ | |
'valid' => $coroutine2->valid(), | |
'current' => $coroutine2->current(), | |
] | |
); | |
$coroutine2->next(); | |
var_dump( | |
['next' => $coroutine2->current()] | |
); | |
exit; | |
//SqlQueue Example | |
$q = new SplQueue(); | |
$q->setIteratorMode(SplDoublyLinkedList::IT_MODE_KEEP); //have two modes available: keep and delete | |
$q->enqueue('item 1'); | |
$q->enqueue('item 2'); | |
$q->enqueue('item 3'); | |
$q->dequeue(); //dequeue Item 1 | |
//$q->dequeue(); | |
//$q->dequeue(); | |
foreach ($q as $item) { | |
echo $item ."\n"; | |
} | |
$q->enqueue('item 4'); | |
foreach ($q as $item) { | |
echo $item ."\n"; | |
} | |
// Pros | |
// -- Coroutines can be used as state machine | |
// -- With coroutines you can write concurrent applications with cooperative MultiTasking | |
// -- With coroutines you can write assync/non-blocking IO applications | |
// Cons | |
// -- You can't managing memory without a JIT Compiler | |
// -- You can't apply Preemptive MultiTasking | |
//Reference | |
// http://sysmagazine.com/posts/164173/ | |
// http://gnugat.github.io/2014/04/30/php-generators-and-coroutines.html | |
// http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/ | |
// http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html | |
// http://php.net/manual/en |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment