PHP 5.5 рдореЗрдВ рд╕рдмрд╕реЗ рдмрдбрд╝реА рдирд╡рд╛рдЪрд╛рд░реЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдЬрдирд░реЗрдЯрд░ рдФрд░ рдХреЛрд░рдЖрдЙрдЯ (рдХреЛрд░рд╛рдЙрдЯрд╛рдЗрди) рдХреЗ рд▓рд┐рдП рд╕рдорд░реНрдерди рд╣реЛрдЧрд╛ред рдЬрдирд░реЗрдЯрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА
рджрд╕реНрддрд╛рд╡реЗрдЬ рдореЗрдВ рдФрд░ рдХрдИ рдЕрдиреНрдп рдкрджреЛрдВ рдкрд░ рдкрд░реНрдпрд╛рдкреНрдд рд░реВрдк рд╕реЗ рдХрд╡рд░ рдХрд┐рдП рдЧрдП рд╣реИрдВ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП,
рдпрд╣ рдпрд╛
рдпрд╣ )ред Coroutines рдХреЛ рдмрд╣реБрдд рдХрдо рдзреНрдпрд╛рди рдорд┐рд▓рд╛ред рдпрд╣ рдмрд╣реБрдд рдЕрдзрд┐рдХ рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рд╣реИ, рд▓реЗрдХрд┐рди рдЯреВрд▓ рдХреЛ рд╕рдордЭрдиреЗ рдФрд░ рд╕рдордЭрд╛рдиреЗ рдореЗрдВ рднреА рдЕрдзрд┐рдХ рдХрдард┐рди рд╣реИред
рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ рджрд┐рдЦрд╛рдКрдВрдЧрд╛ рдХрд┐ рдХреЛрд░рдЯрд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдХрд╛рд░реНрдп рдЕрдиреБрд╕реВрдЪрдХ рдХреЛ рдХреИрд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рдП, рддрд╛рдХрд┐ рдЖрдк рд╕рдордЭ рд╕рдХреЗрдВ рдХрд┐ рдЖрдк рдЙрдирдХреЗ рд╕рд╛рде рдХреНрдпрд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдХреИрд╕реЗ рд▓рд╛рдЧреВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЪрд▓рд┐рдП рдХреБрдЫ рдкрд░рд┐рдЪрдпрд╛рддреНрдордХ рд╢рдмреНрджреЛрдВ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рдпрджрд┐ рдЖрдкрдХреЛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдк рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рдЬрдирд░реЗрдЯрд░ рдФрд░ рдХреЛрд░рдЯрд╛рдЗрди рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рддреБрд░рдВрдд "рд╕рдВрдпреБрдХреНрдд рдорд▓реНрдЯреАрдЯрд╛рд╕реНрдХрд┐рдВрдЧ" рдЕрдиреБрднрд╛рдЧ рдкрд░ рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВред
рдЬрдирд░реЗрдЯрд░
рдЬрдирд░реЗрдЯрд░ рдХрд╛ рд╕рд╛рд░ рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдРрд╕рд╛ рдлрд╝рдВрдХреНрд╢рди рд╣реИ рдЬреЛ рди рдХреЗрд╡рд▓ рдПрдХ рдорд╛рди рд▓реМрдЯрд╛рддрд╛ рд╣реИ, рдмрд▓реНрдХрд┐
рдореВрд▓реНрдпреЛрдВ рдХрд╛ рдПрдХ
рдХреНрд░рдо рд╣реИ , рдЬрд╣рд╛рдВ рдкреНрд░рддреНрдпреЗрдХ рдорд╛рди рдХреЛ рдПрдХ рджреВрд╕рд░реЗ рд╕реЗ рдлреЗрдВрдХ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдпрд╛, рджреВрд╕рд░реЗ рд╢рдмреНрджреЛрдВ рдореЗрдВ, рдЬрдирд░реЗрдЯрд░ рдЖрдкрдХреЛ рдЕрддрд┐рд░рд┐рдХреНрдд рдХреЛрдб рдХреЗ рдмрд┐рдирд╛, рдПрдХ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред
рдПрдХ рдмрд╣реБрдд рд╣реА рд╕рд░рд▓ рдЙрджрд╛рд╣рд░рдг
xrange()
рдлрд╝рдВрдХреНрд╢рди рд╣реИ:
function xrange($start, $end, $step = 1) { for ($i = $start; $i <= $end; $i += $step) { yield $i; } } foreach (xrange(1, 1000000) as $num) { echo $num, "\n"; }
рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд PHP,
range()
ред рдЕрдВрддрд░ рдХреЗрд╡рд▓ рдЗрддрдирд╛ рд╣реИ рдХрд┐ рд╡рд╣
range()
рдЖрдкрдХреЛ рдПрдХ рдорд┐рд▓рд┐рдпрди рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХреА рдПрдХ рд╕рд░рдгреА
xrange()
, рдФрд░
xrange()
рдПрдХ
xrange()
рд▓реМрдЯрд╛рдПрдЧрд╛ рдЬреЛ рдЙрди рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХреЛ рджреВрд░ рдлреЗрдВрдХ рджреЗрдЧрд╛, рд▓реЗрдХрд┐рди рдЙрди рд╕рднреА рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд░рдгреА рдХрднреА рдирд╣реАрдВ рдмрдирд╛рдПрдЧрд╛ред
рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЗ рд▓рд╛рдн рд╕реНрдкрд╖реНрдЯ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рдЗрд╕ рддрд░рд╣ рдЖрдк рдЙрди рд╕рднреА рдХреЛ рдореЗрдореЛрд░реА рдореЗрдВ рд▓реЛрдб рдХрд┐рдП рдмрд┐рдирд╛ рдмрд╣реБрдд рдмрдбрд╝реЗ рдбреЗрдЯрд╛ рд╕реЗрдЯ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рддреБрдо рднреА
рдЕрдВрддрд╣реАрди рдбреЗрдЯрд╛ рдзрд╛рд░рд╛рдУрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдпрд╣ рд╕рдм рдЬреЗрдирд░реЗрдЯрд░ рдХреЗ рдмрд┐рдирд╛ рднреА рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ
Iterator
рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдПрдХ рд╡рд░реНрдЧ рдмрдирд╛рдХрд░ред рдЬрдирд░реЗрдЯрд░ рдХреЗрд╡рд▓ рдЗрд╕реЗ (рдмрд╣реБрдд) рд╕рд░рд▓ рдЕрднреНрдпрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ рдХреНрдпреЛрдВрдХрд┐ рдЖрдкрдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рд▓рд┐рдП рдкрд╛рдВрдЪ рдЕрд▓рдЧ-рдЕрд▓рдЧ рддрд░реАрдХреЛрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред
рдЬрдирд░реЗрдЯрд░реЛрдВ рдореЗрдВ рд░реБрдХрд╛рд╡рдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп
рдЬрдирд░реЗрдЯрд░ рд╕реЗ рдХреЛрд░рдЯрд╛рдЗрди рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдпрд╣ рд╕рдордЭрдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ рд╡реЗ рдЕрдВрджрд░ рд╕реЗ рдХреИрд╕реЗ рдмрдирд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ: рдЬрдирд░реЗрдЯрд░ рдЕрд╡рд░реЛрдзрдХ рдХрд╛рд░реНрдп рд╣реИрдВ рдЬрд┐рд╕рдореЗрдВ
yield
рдХреАрд╡рд░реНрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рд░реБрдХрд╛рд╡рдЯреЛрдВ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдкрд┐рдЫрд▓реЗ рдЙрджрд╛рд╣рд░рдг рдкрд░
xrange(1, 1000000)
, рдЬрдм рдЖрдк
xrange(1, 1000000)
, рддреЛ
xrange
рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд╢рд░реАрд░ рд╕реЗ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреБрдЫ рднреА рдирд╣реАрдВ рдХрд╣рд╛ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, PHP рдмрд╕ рдПрдХ
Generator
рдСрдмреНрдЬреЗрдХреНрдЯ рд▓реМрдЯрд╛рдПрдЧрд╛ рдЬреЛ
Iterator
рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИ:
<?php $range = xrange(1, 1000000); var_dump($range);
рдХреЛрдб рдХреЗрд╡рд▓ рддрднреА рдЪрд▓реЗрдЧрд╛ рдЬрдм рдЖрдк рдХреБрдЫ рдкреБрдирд░рд╛рд╡реГрддреНрдд рддрд░реАрдХреЗ рдЪрд▓рд╛рддреЗ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдпрджрд┐ рдЖрдк
$range->rewind()
рддреЛ рдкрд╣рд▓реА
yield
рд╕реЗ рдкрд╣рд▓реЗ
xrange()
рдлрд╝рдВрдХреНрд╢рди
$range->rewind()
рдХреЛрдб рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐
$i = $start
рдХреЛ рдкрд╣рд▓реЗ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ рдФрд░ рдлрд┐рд░
yield $i
рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рд╣рдо рдЬреЛ рдХреБрдЫ рднреА
yield
рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ, рд╡рд╣
$range->current()
рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ
$range->next()
рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдпрд╣ рдЬрдирд░реЗрдЯрд░ рдХреЛ рдЕрдЧрд▓реА
yield
рддрдХ рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдордЬрдмреВрд░ рдХрд░рддрд╛
yield
ред рдЗрд╕ рдкреНрд░рдХрд╛рд░,
next()
рдФрд░
current()
рд▓рд┐рдП рд▓рдЧрд╛рддрд╛рд░ рдХреЙрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЖрдк рдЬрдирд░реЗрдЯрд░ рд╕реЗ рд╕рднреА рдорд╛рди рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬрдм рддрдХ рдХрд┐ рдпрд╣ рдЙрд╕ рдмрд┐рдВрджреБ рддрдХ рдирд╣реАрдВ рдкрд╣реБрдВрдЪрддрд╛ рдЬрд╣рд╛рдВ рдХреЛрдб рдмрд╕ рд╕рдорд╛рдкреНрдд рд╣реЛрддрд╛ рд╣реИред
xrange()
рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ
xrange()
рдпрд╣ рддрдм рд╣реЛрдЧрд╛ рдЬрдм
$i
$end
рддрдХ рдкрд╣реБрдВрдЪ
$end
ред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдирд┐рд╖реНрдкрд╛рджрди рдХрд╛ рдзрд╛рдЧрд╛ рдЕрдзрд┐рдХ рдХреЛрдб рдЫреЛрдбрд╝рдиреЗ рдХреЗ рдмрд┐рдирд╛ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдЕрдВрдд рддрдХ рдкрд╣реБрдВрдЪрддрд╛ рд╣реИред рдРрд╕рд╛ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж,
valid()
false
рд╡рд╛рдкрд╕ рдЖ рдЬрд╛рдПрдЧрд╛ рдФрд░ рдЪрд▓рдирд╛ рдмрдВрдж рд╣реЛ рдЬрд╛рдПрдЧрд╛ред
coroutine
рдЙрдкрд░реЛрдХреНрдд рдХрд╛рд░реНрдпрд╢реАрд▓рддрд╛ рдореЗрдВ рдореБрдЦреНрдп рдЪреАрдЬрд╝ рдЬреЛ рдХреЛрд░рдЯрд╛рдЗрди рдЬреЛрдбрд╝рддреА рд╣реИ, рд╡рд╣ рдЬрдирд░реЗрдЯрд░ рдХреЛ рдорд╛рди рднреЗрдЬрдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рд╣реИред рдпрд╣ рдЬрдирд░реЗрдЯрд░ рдХреЗ рд╕рд╛рдорд╛рдиреНрдп рдПрдХрд╛рд▓рд╛рдк рдХреЛ рдкреВрд░реНрдг рд╕рдВрд╡рд╛рдж рдмрдирд╛рддрд╛ рд╣реИ, рдЬрд╣рд╛рдВ рдХреЙрд▓рд░ рдЬрдирд░реЗрдЯрд░ рдХреЛ рднреА рдХреБрдЫ рдмрддрд╛ рд╕рдХрддрд╛ рд╣реИред
next()
рдмрдЬрд╛рдп рдХреЙрд▓
send()
рджреНрд╡рд╛рд░рд╛ рдорд╛рдиреЛрдВ рдХреЛ рдХреЛрд░рдЯрд╛рдЗрди рдкрд╛рд╕ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ:
function logger($fileName) { $fileHandle = fopen($fileName, 'a'); while (true) { fwrite($fileHandle, yield . "\n"); } } $logger = logger(__DIR__ . '/log'); $logger->send('Foo'); $logger->send('Bar');
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдпрд╣рд╛рдВ
yield
рдПрдХ рдмрдпрд╛рди (рдЬреИрд╕реЗ
return
рдпрд╛
echo
) рдХреЗ рд░реВрдк рдореЗрдВ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдХреЗ рд░реВрдк рдореЗрдВ, рдпрд╣ рдПрдХ рдореВрд▓реНрдп рд╣реИред рдпрд╣ рд╡рд╣
send()
рдЬреЛ рднреЗрдЬреЗ
send()
рдорд╛рдзреНрдпрдо рд╕реЗ рднреЗрдЬрд╛ рдЧрдпрд╛ рдерд╛ред рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ,
yield
"Foo"
рдФрд░ рдлрд┐рд░
"Bar"
рд╡рд╛рдкрд╕ рдЖ
yield
ред
рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, рдпрд╣ рдкреНрд░рд╕реНрддреБрдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдХрд┐
yield
рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдкреНрд░рд╛рдкреНрддрдХрд░реНрддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдХреИрд╕реЗ рдХрд╛рд░реНрдп рдХрд░ рд╕рдХрддреА рд╣реИред рд▓реЗрдХрд┐рди рдЖрдк рджреЛрдиреЛрдВ рдкреНрд░рдХрд╛рд░ рдХреЗ рдЙрдкрдпреЛрдЧ рдХреЛ рднреА рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдЖрдк рдорд╛рди рднреЗрдЬ рдФрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдЗрд╕рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ:
function gen() { $ret = (yield 'yield1'); var_dump($ret); $ret = (yield 'yield2'); var_dump($ret); } $gen = gen(); var_dump($gen->current()); // string(6) "yield1" var_dump($gen->send('ret1')); // string(4) "ret1" (the first var_dump in gen) // string(6) "yield2" (the var_dump of the ->send() return value) var_dump($gen->send('ret2')); // string(4) "ret2" (again from within gen) // NULL (the return value of ->send())
рд╕рдЯреАрдХ рдЖрдЙрдЯрдкреБрдЯ рдСрд░реНрдбрд░ рдкрд╣рд▓реА рдирдЬрд╝рд░ рдореЗрдВ рд╕рдордЭрдиреЗ рдореЗрдВ рдереЛрдбрд╝рд╛ рдореБрд╢реНрдХрд┐рд▓ рд▓рдЧ рд╕рдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдХреЛрдб рдХреЛ рдлрд┐рд░ рд╕реЗ рдкрдврд╝реЗрдВ рдФрд░ рдпрд╣ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП рдЦреБрдж рдХреЛ рдЪрд▓рд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реЗрдВ рдХрд┐ рдЙрд╕ рдХреНрд░рдо рдореЗрдВ рд╕рдм рдХреБрдЫ рдХреНрдпреЛрдВ рд╣реЛрддрд╛ рд╣реИред рдпрд╣рд╛рдВ рджреЛ рдЪреАрдЬреЗрдВ рд╣реИрдВ рдЬреЛ рдореИрдВ рдЖрдкрдХрд╛ рдзреНрдпрд╛рди рдЖрдХрд░реНрд╖рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ: рдкрд╣рд▓рд╛,
yield
рдЖрд╕рдкрд╛рд╕ рдХреЛрд╖реНрдардХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреЛрдИ рджреБрд░реНрдШрдЯрдирд╛ рдирд╣реАрдВ рд╣реИред рд╣рдореЗрдВ рддрдХрдиреАрдХреА рдХрд╛рд░рдгреЛрдВ рд╕реЗ рдЗрди рдХреЛрд╖реНрдардХреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ (рдореИрдВрдиреЗ рдкреНрд░рддреНрдпрдХреНрд╖ рдЕрд╕рд╛рдЗрдирдореЗрдВрдЯ рдореЗрдВ рдПрдХ рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХ рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рднреА рд╕реЛрдЪрд╛ рдерд╛)ред рджреВрд╕рд░реЗ, рдЖрдкрдиреЗ рджреЗрдЦрд╛ рд╣реЛрдЧрд╛ рдХрд┐
current()
рдХрд╛ рдЙрдкрдпреЛрдЧ
rewind()
рдмрд┐рдирд╛ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред
rewind()
, рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рддрдм рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рд░реВрдк рд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рд╕рд╣рдпреЛрдЧреА рдорд▓реНрдЯреАрдЯрд╛рд╕реНрдХрд┐рдВрдЧ
рдпрджрд┐ рдЖрдк рдЙрджрд╛рд╣рд░рдг
logger()
рдлрд╝рдВрдХреНрд╢рди рдкрдврд╝рддреЗ рд╣реИрдВ, рддреЛ рдЖрдкрдиреЗ рд╕реЛрдЪрд╛, тАЬрдореИрдВ рдЗрд╕рдХреЗ рд▓рд┐рдП рдХреЛрд░рдЯрд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреНрдпреЛрдВ рдХрд░реВрдВрдЧрд╛? рдЗрд╕рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡рд░реНрдЧ рдХреНрдпреЛрдВ рдирд╣реАрдВ рдмрдирд╛рдпрд╛ рдЧрдпрд╛? тАЭ, рддреЛ рдЖрдк рдмрд┐рд▓реНрдХреБрд▓ рд╕рд╣реА рдереЗред рдпрд╣ рдЙрджрд╛рд╣рд░рдг рдХреЗрд╡рд▓ рдпрд╣ рджрд░реНрд╢рд╛рддрд╛ рд╣реИ рдХрд┐ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдХреЛрд░рдЯрд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдХреЛрдИ рдХрд╛рд░рдг рдирд╣реАрдВ рд╣реИред рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдкрд░рд┐рдЪрдп рдореЗрдВ, рдХреЛрд░рдЯрд╛рдЗрди рдПрдХ рдмрд╣реБрдд рд╣реА рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рдЪреАрдЬ рд╣реИ, рд▓реЗрдХрд┐рди рдЙрдирдХрд╛ рдЙрдкрдпреЛрдЧ рдмрд╣реБрдд рд╣реА рджреБрд░реНрд▓рдн рдФрд░ рдЕрдХреНрд╕рд░ рдмрд╣реБрдд рдЬрдЯрд┐рд▓ рд╣реЛрддрд╛ рд╣реИ, рдЬреЛ рд╕рд░рд▓ рдФрд░ рджреВрд░-рджрд░рд╛рдЬ рдХреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЗ рд╕рд╛рде рдЖрдиреЗ рдХрд╛ рдХрд╛рд░реНрдп рдХрд╛рдлреА рдХрдард┐рди рдмрдирд╛ рджреЗрддрд╛ рд╣реИред
рдореИрдВрдиреЗ рдЖрдкрдХреЛ рдХреЛрд░рдЯрд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕рд╣рдпреЛрдЧреА рдорд▓реНрдЯреАрдЯрд╛рд╕реНрдХрд┐рдВрдЧ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рджрд┐рдЦрд╛рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ред рд▓рдмреНрдмреЛрд▓реБрдЖрдм рдпрд╣ рд╣реИ рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХрдИ рдХрд╛рд░реНрдп рд╣реИрдВ рдЬрд┐рдиреНрд╣реЗрдВ рд╕рдорд╛рдирд╛рдВрддрд░ рдореЗрдВ рдЪрд▓рд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдПрдХ рдкреНрд░реЛрд╕реЗрд╕рд░ (рд▓рдЧрднрдЧ рд▓реЗрдиред рдЧреЛрд▓рд╛рдХрд╛рд░ рдФрд░ рд╡реИрдХреНрдпреВрдо рдореЗрдВ) рдПрдХ рд╕рдордп рдореЗрдВ рдХреЗрд╡рд▓ рдПрдХ рд╣реА рдХрд╛рд░реНрдп рдХрд░ рд╕рдХрддрд╛ рд╣реИред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдкреНрд░реЛрд╕реЗрд╕рд░ рдХреЛ рд╡рд┐рднрд┐рдиреНрди рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдмреАрдЪ рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рдФрд░ рдкреНрд░рддреНрдпреЗрдХ "рдереЛрдбрд╝рд╛ рдХрд╛рдо" рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред
"рдЬреЙрдЗрдВрдЯ" рдпрд╣ рдорд▓реНрдЯреАрдЯрд╛рд╕реНрдХрд┐рдВрдЧ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдореЗрдВ рд╢реЗрдбреНрдпреВрд▓рд░ рдХреЛ рдирд┐рд╖реНрдкрд╛рджрди рдХрд╛ рдирд┐рдпрдВрддреНрд░рдг рд╕реНрд╡реЗрдЪреНрдЫрд╛ рд╕реЗ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдирд╛ рд╢рд╛рдорд┐рд▓ рд╣реИ рддрд╛рдХрд┐ рд╡рд╣ рдПрдХ рдФрд░ рдХрд╛рд░реНрдп рд╢реБрд░реВ рдХрд░ рд╕рдХреЗред рдкреНрд░реАрдореЗрдкреНрдЯрд┐рд╡ рдорд▓реНрдЯреАрдЯрд╛рд╕реНрдХрд┐рдВрдЧ рднреА рд╣реИ, рдЬрд╣рд╛рдБ рд╢реЗрдбреНрдпреВрд▓рд░ рд╕реНрд╡рдпрдВ рдХрд╛рд░реНрдп рдХреЛ рдмрд╛рдзрд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред рд╕рд╣рдпреЛрдЧрд╛рддреНрдордХ рдорд▓реНрдЯреАрдЯрд╛рд╕реНрдХрд┐рдВрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╡рд┐рдВрдбреЛрдЬ рдХреЗ рдкреБрд░рд╛рдиреЗ рд╕рдВрд╕реНрдХрд░рдгреЛрдВ (Win95 рд╕реЗ рдкрд╣рд▓реЗ) рдФрд░ рдореИрдХ рдУрдПрд╕ рдореЗрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рд▓реЗрдХрд┐рди рдлрд┐рд░ рдЙрдиреНрд╣реЛрдВрдиреЗ рдкреНрд░реАрдореЗрдкреНрдЯрд┐рд╡ рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░ рджрд┐рдпрд╛ред рдХрд╛рд░рдг рд╕реНрдкрд╖реНрдЯ рд╣реИ - рдпрджрд┐ рдЖрдк рдирд┐рдпрдВрддреНрд░рдг рдкреНрд░рд╡рд╛рд╣ рдХреЛ рд╕реНрд╡реЗрдЪреНрдЫрд╛ рд╕реЗ рдЫреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рд╕реА рднреА рдХрд╛рд░реНрдпрдХреНрд░рдо рдкрд░ рднрд░реЛрд╕рд╛ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдХреЛрдИ рднреА рдХрд╛рд░реНрдпрдХреНрд░рдо рдХреЗрд╡рд▓ рд╕рдВрдкреВрд░реНрдг рд╕реАрдкреАрдпреВ рдкрд░ рдХрдмреНрдЬрд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реИред
рдЕрдм рдЖрдкрдХреЛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдХреЛрд░рдЯрд╛рдЗрди рдФрд░ рдЯрд╛рд╕реНрдХ рд╢реЗрдбреНрдпреВрд▓рд░ рдХреЗ рдмреАрдЪ рд╕рдВрдмрдВрдз рджреЗрдЦрдирд╛ рдЪрд╛рд╣рд┐рдП:
yield
рдЖрдкрдХреЛ рд╢реЗрдбреНрдпреВрд▓рд░ рдирд┐рдпрдВрддреНрд░рдг рдкреНрд░рд╡рд╛рд╣ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рд░реНрдп рдХреЛ рд╕реНрд╡рдпрдВ рдмрд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдФрд░ рдпрд╣ рдПрдХ рдФрд░ рдХрд╛рд░реНрдп рд╢реБрд░реВ рдХрд░ рд╕рдХрддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрддрд┐рд░рд┐рдХреНрдд, рд╢реЗрдбреНрдпреВрд▓рд░ рдХреЗ рд╕рд╛рде рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд╕рдВрдкреНрд░реЗрд╖рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП
yield
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдХрд╛рд░реНрдп рдЬрдирд░реЗрдЯрд░ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдЖрд╕рдкрд╛рд╕ рдПрдХ рдкрддрд▓рд╛ рдЖрд╡рд░рдг рд╣реЛрдЧрд╛:
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) { $this->beforeFirstYield = false; return $this->coroutine->current(); } else { $retval = $this->coroutine->send($this->sendValue); $this->sendValue = null; return $retval; } } public function isFinished() { return !$this->coroutine->valid(); } }
рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рд░реНрдп рдХрд╛ рдЕрдкрдирд╛ рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛ (
taskId
) рд╣реЛрдЧрд╛ред
setSendValue()
рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЖрдк рдЕрдЧрд▓реЗ рд░рди рдкрд░ рдХрд╛рд░реНрдп рдХреЛ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрдпрд╛ рдореВрд▓реНрдп рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ (рдЖрдкрдХреЛ рдмрд╛рдж рдореЗрдВ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рд╣реИ)ред
run()
рд╡рд┐рдзрд┐ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╕рд┐рд░реНрдл рдХреЙрд░рдЯрд╛рдЗрдиреНрд╕
send()
рд╡рд┐рдзрд┐ рдХрд╣рддреА рд╣реИред
рдпрд╣ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рд╣рдореЗрдВ рдкрд╣рд▓реЗ
beforeFirstYield
рдлрд╝реНрд▓реИрдЧ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рд╣реИ, рдирд┐рдореНрди рдХреЛрдб рдХреЛ рджреЗрдЦреЗрдВ:
function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); var_dump($gen->send('something')); // send(), yield rewind() // : $gen->rewind(); var_dump($gen->send('something')); // rewind() yield ( ), send() // yield ( var_dump- ). //
beforeFirstYield
рд╕рд╛рде
beforeFirstYield
рд╣рдореЗрдВ рдкрддрд╛ рдЪрд▓ рдЬрд╛рдПрдЧрд╛ рдХрд┐ рдХреНрдпрд╛ рдкрд╣рд▓реА рдЙрдкрдЬ рдкрд╣рд▓реЗ рд╣реА рд╡рд╛рдкрд╕ рдЖ рдЧрдИ рд╣реИред
рд╢реЗрдбреНрдпреВрд▓рд░ рдХреЛ рдЕрдм рд╕рднреА рдХрд╛рд░реНрдпреЛрдВ рд╕реЗ рдереЛрдбрд╝рд╛ рдЕрдзрд┐рдХ рдЪрд▓рдирд╛ рд╣реЛрдЧрд╛:
class Scheduler { protected $maxTaskId = 0; protected $taskMap = []; // taskId => task protected $taskQueue; public function __construct() { $this->taskQueue = new SplQueue(); } public function newTask(Generator $coroutine) { $tid = ++$this->maxTaskId; $task = new Task($tid, $coroutine); $this->taskMap[$tid] = $task; $this->schedule($task); return $tid; } public function schedule(Task $task) { $this->taskQueue->enqueue($task); } public function run() { while (!$this->taskQueue->isEmpty()) { $task = $this->taskQueue->dequeue(); $task->run(); if ($task->isFinished()) { unset($this->taskMap[$task->getTaskId()]); } else { $this->schedule($task); } } } }
newTask()
рд╡рд┐рдзрд┐ рдПрдХ рдирдпрд╛ рдХрд╛рд░реНрдп рдмрдирд╛рддреА рд╣реИ рдФрд░ рдЗрд╕реЗ
taskMap
рдореЗрдВ
taskMap
ред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╡рд╣ рдЗрд╕реЗ
taskQueue
рдЬреЛрдбрд╝рддрд╛ рд╣реИред
run()
рд╡рд┐рдзрд┐ рддрдм рдЗрд╕ рдХрддрд╛рд░ рд╕реЗ рдЧреБрдЬрд░рддреА рд╣реИ рдФрд░ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд╢реБрд░реВ рдХрд░рддреА рд╣реИред рдпрджрд┐ рдХрд╛рд░реНрдп рдкреВрд░рд╛ рд╣реЛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ рд╣рдЯрд╛ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЕрдиреНрдпрдерд╛ рдЗрд╕реЗ рдХрддрд╛рд░ рдХреЗ рдЕрдВрдд рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред
рдЖрдЗрдП рдПрдХ рдЕрдиреБрд╕реВрдЪрдХ рдХреЛ рджреЛ рд╕рд░рд▓ (рдФрд░ рдЕрд░реНрдерд╣реАрди) рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд╕рд╛рде рдЖрдЬрд╝рдорд╛рдПрдБ:
function task1() { for ($i = 1; $i <= 10; ++$i) { echo "This is task 1 iteration $i.\n"; yield; } } function task2() { for ($i = 1; $i <= 5; ++$i) { echo "This is task 2 iteration $i.\n"; yield; } } $scheduler = new Scheduler; $scheduler->newTask(task1()); $scheduler->newTask(task2()); $scheduler->run();
рджреЛрдиреЛрдВ рдХрд╛рд░реНрдп рдХреЗрд╡рд▓ рдПрдХ рд╕рдВрджреЗрд╢ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдХрдВрдЯреНрд░реЛрд▓рд░ рдХреЛ рд╡рд╛рдкрд╕ рд╢реЗрдбреНрдпреВрд▓рд░ рдХреЛ рджреЗрддреЗ рд╣реИрдВред рдпрд╣рд╛рдБ рдЖрдЙрдЯрдкреБрдЯ рдХреНрдпрд╛ рд╣реЛрдЧрд╛:
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 1 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 2 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 1 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 2 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 2 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 2 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 3 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 2 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 3 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 4 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 2 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 4 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 5 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 2 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 5 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 6 тАЛтАЛрд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 7 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 8 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 9 рд╣реИред
рдпрд╣ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 10 рд╣реИред
рд╢реЗрдбреНрдпреВрд▓рд░ рдЗрдВрдЯрд░реИрдХреНрд╢рди
рдЗрд╕рд▓рд┐рдП, рд╣рдорд╛рд░рд╛ рд╢реЗрдбреНрдпреВрд▓рд░ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╣рдо рдПрдЬреЗрдВрдбреЗ рдкрд░ рдЕрдЧрд▓реЗ рдЖрдЗрдЯрдо рдкрд░ рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ: рдХрд╛рд░реНрдпреЛрдВ рдФрд░ рдЕрдиреБрд╕реВрдЪрдХ рдХреЗ рдмреАрдЪ рдмрд╛рддрдЪреАрддред рд╣рдо рдЙрд╕реА рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ рдЬреЛ рдкреНрд░рдХреНрд░рд┐рдпрд╛рдПрдВ рдСрдкрд░реЗрдЯрд┐рдВрдЧ рд╕рд┐рд╕реНрдЯрдо рдХреЗ рд╕рд╛рде рд╕рдВрдЪрд╛рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░рддреА рд╣реИрдВ: рд╕рд┐рд╕реНрдЯрдо рдХреЙрд▓ (syscall, siskol)ред рд╣рдореЗрдВ syskols рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдСрдкрд░реЗрдЯрд┐рдВрдЧ рд╕рд┐рд╕реНрдЯрдо рд╕реНрд╡рдпрдВ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд╡рд┐рд╢реЗрд╖рд╛рдзрд┐рдХрд╛рд░реЛрдВ рдХреЗ рдПрдХ рдЕрд▓рдЧ рд╕реНрддрд░ рдкрд░ рд╣реИред рдЗрд╕рд▓рд┐рдП, рдХреБрдЫ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдиреНрд╣реЗрдВ рдЕрдзрд┐рдХ рд╡рд┐рд╢реЗрд╖рд╛рдзрд┐рдХрд╛рд░реЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЕрдиреНрдп рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рдорд╛рд░рдирд╛), рдХрд░реНрдиреЗрд▓ рдХреЛ рдирд┐рдпрдВрддреНрд░рдг рджреЗрдиреЗ рдХрд╛ рдПрдХ рддрд░реАрдХрд╛ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рддрд╛рдХрд┐ рдпрд╣ рдЙрди рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд░ рд╕рдХреЗред рдЖрдВрддрд░рд┐рдХ рд░реВрдк рд╕реЗ, рдпрд╣ рд░реБрдХрд╛рд╡рдЯ рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рд╣рдорд╛рд░рд╛ рд╢реЗрдбреНрдпреВрд▓рд░ рдЗрд╕ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдХрд╛ рдкрд╛рд▓рди рдХрд░реЗрдЧрд╛: рд╢реЗрдбреНрдпреВрд▓рд░ рдХреЛ рдХрд╛рд░реНрдп рдХреЗ рдкрд╛рд╕ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп (рдЬреЛ рдЗрд╕рдХреЗ рд╕рд╛рде рдХреБрдЫ рднреА рдХрд░ рд╕рдХрддрд╛ рд╣реИ), рд╣рдо
yield
рдорд╛рдзреНрдпрдо рд╕реЗ рдкрд╛рд░рд┐рдд рдкрд╛рд╕рд╡рд░реНрдб рдХреА рдорджрдж рд╕реЗ рдмрд╛рддрдЪреАрдд рдХрд░реЗрдВрдЧреЗред рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ,
yield
рдПрдХ рдмреНрд░реЗрдХрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдФрд░ рдЕрдиреБрд╕реВрдЪрдХ рдХреЛ рд╕реВрдЪрдирд╛ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд░рдиреЗ (рдФрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ) рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд░реЗрдЧрд╛ред
рд╕рд┐рд╕реНрдХреЛрд▓ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВ рдХреЙрд▓ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рдкрд░ рдПрдХ рдЫреЛрдЯреЗ рдЖрд╡рд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реВрдВрдЧрд╛:
class SystemCall { protected $callback; public function __construct(callable $callback) { $this->callback = $callback; } public function __invoke(Task $task, Scheduler $scheduler) { $callback = $this->callback;
рдпрд╣ рдХрд┐рд╕реА рднреА рдЕрдиреНрдп
callable
рдпреЛрдЧреНрдп рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд░реЗрдЧрд╛, рд▓реЗрдХрд┐рди рдХрд╛рд░реНрдп рдФрд░ рдЕрдиреБрд╕реВрдЪрдХ рдХреЛ рддрд░реНрдХреЛрдВ рдореЗрдВ рд▓реЗ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рд╢реЗрдбреНрдпреВрд▓рд░ рдХреА
run()
рд╡рд┐рдзрд┐ рдХреЛ рдереЛрдбрд╝рд╛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛:
public function run() { while (!$this->taskQueue->isEmpty()) { $task = $this->taskQueue->dequeue(); $retval = $task->run(); if ($retval instanceof SystemCall) { $retval($task, $this); continue; } if ($task->isFinished()) { unset($this->taskMap[$task->getTaskId()]); } else { $this->schedule($task); } } }
рд╣рдорд╛рд░рд╛ рдкрд╣рд▓рд╛ syskol рдмрд╕ рдХрд╛рд░реНрдп рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛ рдХреЛ рд▓реМрдЯрд╛рдПрдЧрд╛:
function getTaskId() { return new SystemCall(function(Task $task, Scheduler $scheduler) { $task->setSendValue($task->getTaskId()); $scheduler->schedule($task); }); }
рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП рд╣реЛрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдорд╛рди рд╕реЗрдЯ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдХрд╛рд░реНрдп рдХреЛ рд╢реЗрдбреНрдпреВрд▓рд░ рдкрд░ рд╡рд╛рдкрд╕ рд╕реЗрдЯ рдХрд░рддреЗ рд╣реИрдВред Syskol рдХреЗ рд▓рд┐рдП, рдЕрдиреБрд╕реВрдЪрдХ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдХрддрд╛рд░ рдореЗрдВ рдХрд╛рд░реНрдп рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рд╣рдореЗрдВ рдЗрд╕реЗ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ (рдЖрдкрдХреЛ рдкрддрд╛ рдЪрд▓ рдЬрд╛рдПрдЧрд╛ рдХрд┐ рдереЛрдбрд╝реА рджреЗрд░ рдмрд╛рдж рдХреНрдпреЛрдВ)ред
рдЗрд╕ рдирдП рд╕рд┐рд╕реНрдХреЛрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рд╣рдо рдкрд┐рдЫрд▓реЗ рдЙрджрд╛рд╣рд░рдг рдХреЛ рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ:
function task($max) { $tid = (yield getTaskId()); // <-- here's the syscall! for ($i = 1; $i <= $max; ++$i) { echo "This is task $tid iteration $i.\n"; yield; } } $scheduler = new Scheduler; $scheduler->newTask(task(10)); $scheduler->newTask(task(5)); $scheduler->run();
рдпрд╣ рдХреЛрдб рд╣рдореЗрдВ рдкрд┐рдЫрд▓реЗ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд╕рдорд╛рди рдЖрдЙрдЯрдкреБрдЯ рджреЗрдЧрд╛ред рдирдП рдФрд░ рдорд╛рд░ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЬреЛрдбрд╝реА siskols:
function newTask(Generator $coroutine) { return new SystemCall( function(Task $task, Scheduler $scheduler) use ($coroutine) { $task->setSendValue($scheduler->newTask($coroutine)); $scheduler->schedule($task); } ); } function killTask($tid) { return new SystemCall( function(Task $task, Scheduler $scheduler) use ($tid) { $task->setSendValue($scheduler->killTask($tid)); $scheduler->schedule($task); } ); }
killTask
рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд▓рд┐рдП
killTask
рд╣рдореЗрдВ рд╢реЗрдбреНрдпреВрд▓рд░ рдореЗрдВ рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рд╡рд┐рдзрд┐ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:
public function killTask($tid) { if (!isset($this->taskMap[$tid])) { return false; } unset($this->taskMap[$tid]); // , // , foreach ($this->taskQueue as $i => $task) { if ($task->getTaskId() === $tid) { unset($this->taskQueue[$i]); break; } } return true; }
рдЗрд╕ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЫреЛрдЯреА рд╕реНрдХреНрд░рд┐рдкреНрдЯ:
function childTask() { $tid = (yield getTaskId()); while (true) { echo "Child task $tid still alive!\n"; yield; } } function task() { $tid = (yield getTaskId()); $childTid = (yield newTask(childTask())); for ($i = 1; $i <= 6; ++$i) { echo "Parent task $tid iteration $i.\n"; yield; if ($i == 3) yield killTask($childTid); } } $scheduler = new Scheduler; $scheduler->newTask(task()); $scheduler->run();
рдирд┐рд╖реНрдХрд░реНрд╖ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реЛрдЧрд╛:
рдореВрд▓ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 1ред
рдмрд╛рд▓ рдХрд╛рд░реНрдп 2 рдЕрднреА рднреА рдЬреАрд╡рд┐рдд рд╣реИ!
рдореВрд▓ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 2ред
рдмрд╛рд▓ рдХрд╛рд░реНрдп 2 рдЕрднреА рднреА рдЬреАрд╡рд┐рдд рд╣реИ!
рдореВрд▓ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 3ред
рдмрд╛рд▓ рдХрд╛рд░реНрдп 2 рдЕрднреА рднреА рдЬреАрд╡рд┐рдд рд╣реИ!
рдореВрд▓ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 4ред
рдореВрд▓ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 5ред
рдореВрд▓ рдХрд╛рд░реНрдп 1 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ 6ред
рдмрд╛рд▓ рдХрд╛рд░реНрдп рддреАрди рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рдХреЗ рдмрд╛рдж рдорд╛рд░рд╛ рдЬрд╛рддрд╛ рд╣реИ, рдлрд┐рд░ "рдмрд╛рд▓ рдХрд╛рд░реНрдп рдЕрднреА рднреА рдЬреАрд╡рд┐рдд рд╣реИ!" рд╕рдВрджреЗрд╢ рд╕рдорд╛рдкреНрдд рд╣реЛрддрд╛ рд╣реИред рдпрд╣ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдорд╛рддрд╛-рдкрд┐рддрд╛ рдФрд░ рдмрдЪреНрдЪрд╛ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдмрдЪреНрдЪреЗ рдХреЗ рдХрд╛рд░реНрдп рдХреЛ рддрдм рднреА рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬрдм рдорд╛рддрд╛-рдкрд┐рддрд╛ рдкрд╣рд▓реЗ рд╣реА рдкреВрд░рд╛ рдХрд░ рдЪреБрдХреЗ рд╣реЛрдВред рдпрд╛ рдмреЗрдЯреА рдорд╛рддрд╛-рдкрд┐рддрд╛ рдХреЛ рднреА рдорд╛рд░ рд╕рдХрддреА рд╣реИред рд╣рдо рдорд╛рддрд╛-рдкрд┐рддрд╛ рдФрд░ рдмрдЪреНрдЪреЗ рдХреЗ рдмреАрдЪ рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рд╕рдВрдмрдВрдз рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд╕реВрдЪрдХ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдирд╣реАрдВред
рдХреБрдЫ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкреНрд░рдХрд╛рд░ рдХреА рдХреЙрд▓ рд╣реИрдВ рдЬрд┐рдиреНрд╣реЗрдВ рдЖрдк рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреИрд╕реЗ рдХрд┐
wait
(рдХрд╛рд░реНрдп рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕рдорд╛рдкреНрдд рд╣реЛрдиреЗ рддрдХ рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░реЗрдВ),
exec
(рдЬреЛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдЕрдм рдХрд┐рд╕ рдХрд╛рд░реНрдп рдХреЛ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ) рдФрд░
fork
(рдХрд╛рд░реНрдп рдХрд╛ рдХреНрд▓реЛрди рдмрдирд╛рдирд╛)ред рдХреНрд▓реЛрдирд┐рдВрдЧ рдПрдХ рдмрд╣реБрдд рдЕрдЪреНрдЫреА рд╕реБрд╡рд┐рдзрд╛ рд╣реИ рдФрд░ рдЖрдк рдЗрд╕реЗ рдХреЛрд░рдЯрд╛рдЗрди рдХреЗ рд╕рд╛рде рд▓рд╛рдЧреВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рд╡реЗ рдХреНрд▓реЛрдирд┐рдВрдЧ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддреЗ рд╣реИрдВред
рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз рдмрд╛рддрдЪреАрдд
рдПрдХ рд╡реЗрдм рд╕рд░реНрд╡рд░ - рд╣рдорд╛рд░реЗ рдЕрдиреБрд╕реВрдЪрдХ рдХрд╛ рдПрдХ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╕реНрдкрд╖реНрдЯ рд╣реИред рдирдП рдХрдиреЗрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рд╕реЙрдХреЗрдЯ рдкрд░ рдПрдХ рдХрд╛рд░реНрдп рд╕реБрдирдирд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рд╣рд░ рдмрд╛рд░ рдПрдХ рдирдпрд╛ рдХрдиреЗрдХреНрд╢рди рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЗрд╕ рдХрдиреЗрдХреНрд╢рди рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдирдпрд╛ рдХрд╛рд░реНрдп рдмрдирд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛ред
рдХрдард┐рдирд╛рдИ рдЗрд╕ рддрдереНрдп рдореЗрдВ рдирд┐рд╣рд┐рдд рд╣реИ рдХрд┐ PHP рдореЗрдВ рдбреЗрдЯрд╛ рдкрдврд╝рдиреЗ рдЬреИрд╕реЗ рд╕реЙрдХреЗрдЯреНрд╕ рдкрд░ рд╕рдВрдЪрд╛рд▓рди рдЕрд╡рд░реБрджреНрдз рд╣реЛ рд░рд╣рд╛ рд╣реИ, рдЕрд░реНрдерд╛рдд, рдЬрдм рддрдХ рдХреНрд▓рд╛рдЗрдВрдЯ рдбреЗрдЯрд╛ рднреЗрдЬрдиреЗ рдХрд╛ рдХрд╛рдо рдкреВрд░рд╛ рдирд╣реАрдВ рдХрд░реЗрдЧрд╛, рддрдм рддрдХ PHP рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░реЗрдЧрд╛ред рдПрдХ рд╡реЗрдм рд╕рд░реНрд╡рд░ рдХреЗ рд▓рд┐рдП, рдпрд╣ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдЕрдЪреНрдЫрд╛ рдирд╣реАрдВ рд╣реИ: рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдпрд╣ рдХреЗрд╡рд▓ рдПрдХ рд╕рдордп рдореЗрдВ рдПрдХ рдЕрдиреБрд░реЛрдз рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред
рд╕рдорд╛рдзрд╛рди рдХреЗ рд░реВрдк рдореЗрдВ, рд╣рдореЗрдВ рд╕реЙрдХреЗрдЯ рд╕реЗ рдкреВрдЫрдирд╛ рд╣реЛрдЧрд╛ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдбреЗрдЯрд╛ рдкрдврд╝рдиреЗ рдпрд╛ рд▓рд┐рдЦрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ "рддреИрдпрд╛рд░" рд╣реИред рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдХреМрди рд╕реЗ рд╕реЙрдХреЗрдЯ рдбреЗрдЯрд╛ рднреЗрдЬрдиреЗ рдпрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рддреИрдпрд╛рд░ рд╣реИрдВ, рд╣рдо
stream_select()
рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред
рдЖрд░рдВрдн рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдЗрдП рдХреБрдЫ рдирдП рд╕рд┐рд╕реНрдХреЛрд▓реНрд╕ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ рдЬреЛ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╕реЙрдХреЗрдЯ рднреЗрдЬреЗрдВрдЧреЗ рдЬреЛ рдпрд╛ рддреЛ рдкрдврд╝рдиреЗ рдпрд╛ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдЗрдВрддрдЬрд╛рд░ рдХрд░ рд░рд╣рд╛ рд╣реИ:
function waitForRead($socket) { return new SystemCall( function(Task $task, Scheduler $scheduler) use ($socket) { $scheduler->waitForRead($socket, $task); } ); } function waitForWrite($socket) { return new SystemCall( function(Task $task, Scheduler $scheduler) use ($socket) { $scheduler->waitForWrite($socket, $task); } ); }
рдпреЗ рд╕рд┐рд╕реНрдХреЛрд▓реНрд╕ рдЗрд╕реА рдЕрдиреБрд╕реВрдЪрдХ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рдореАрдкрд╡рд░реНрддреА рд╣реИрдВ:
<?php [socket, tasks] protected $waitingForRead = []; protected $waitingForWrite = []; public function waitForRead($socket, Task $task) { if (isset($this->waitingForRead[(int) $socket])) { $this->waitingForRead[(int) $socket][1][] = $task; } else { $this->waitingForRead[(int) $socket] = [$socket, [$task]]; } } public function waitForWrite($socket, Task $task) { if (isset($this->waitingForWrite[(int) $socket])) { $this->waitingForWrite[(int) $socket][1][] = $task; } else { $this->waitingForWrite[(int) $socket] = [$socket, [$task]]; } }
waitingForRead
рдФрд░
waitingForWrite
рдмрд╕ рдЗрдВрддрдЬрд╝рд╛рд░ рдХрд░ рд░рд╣реЗ рд╕реЙрдХреЗрдЯреНрд╕ рдФрд░ рд╕рдВрдмрдВрдзрд┐рдд рдХрд╛рд░реНрдп рд╣реИрдВред рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣рд┐рд╕реНрд╕рд╛ рдпрд╣ рд╡рд┐рдзрд┐ рд╣реИ, рдЬреЛ рдпрд╣ рдЬрд╛рдВрдЪрддреА рд╣реИ рдХрд┐ рдХреНрдпрд╛ рд╕реЙрдХреЗрдЯ рддреИрдпрд╛рд░ рд╣реИрдВ рдФрд░ рдЙрдирдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдлрд┐рд░ рд╕реЗ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИ:
protected function ioPoll($timeout) { $rSocks = []; foreach ($this->waitingForRead as list($socket)) { $rSocks[] = $socket; } $wSocks = []; foreach ($this->waitingForWrite as list($socket)) { $wSocks[] = $socket; } $eSocks = []; // dummy if (!stream_select($rSocks, $wSocks, $eSocks, $timeout)) { return; } foreach ($rSocks as $socket) { list(, $tasks) = $this->waitingForRead[(int) $socket]; unset($this->waitingForRead[(int) $socket]); foreach ($tasks as $task) { $this->schedule($task); } } foreach ($wSocks as $socket) { list(, $tasks) = $this->waitingForWrite[(int) $socket]; unset($this->waitingForWrite[(int) $socket]); foreach ($tasks as $task) { $this->schedule($task); } } }
stream_select
рдлрд╝рдВрдХреНрд╢рди рд╕реЙрдХреЗрдЯ рдХреЗ рдЗрдирдкреБрдЯ рдПрд░реЗ рдореЗрдВ рд▓реЗрддрд╛ рд╣реИ рдЬреЛ рдкрдврд╝рдиреЗ, рд▓рд┐рдЦреЗ рдЬрд╛рдиреЗ рдФрд░ рдлреЗрдВрдХреЗ рдЬрд╛рдиреЗ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдореЗрдВ рд╣реИ (рдмрд╛рдж рд╡рд╛рд▓реЗ рдХреЛ рдЕрдирджреЗрдЦрд╛ рдХрд░реЗрдВ)ред рдПрд░реЗ рдХреЛ рд╕рдВрджрд░реНрдн рджреНрд╡рд╛рд░рд╛ рдкрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдХреЗрд╡рд▓ рдЙрди рддрддреНрд╡реЛрдВ рдХреЛ рдЫреЛрдбрд╝ рджреЗрдЧрд╛ рдЬрд┐рдирдХреА рд╕реНрдерд┐рддрд┐ рдмрджрд▓ рдЧрдИ рд╣реИред рдЙрд╕рдХреЗ рдмрд╛рдж рд╣рдо рдЗрди рд╕рднреА рд╕рд░рдгрд┐рдпреЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЗрди рд╕реЙрдХреЗрдЯреНрд╕ рд╕реЗ рдЬреБрдбрд╝реЗ рд╕рднреА рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкреБрдирд░реНрдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдЗрди рд╕рднреА рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╡рд┐рдзрд┐ рдХреЛ рдЕрдиреБрд╕реВрдЪрдХ рдореЗрдВ рдЬреЛрдбрд╝ рджреЗрдВрдЧреЗ:
protected function ioPollTask() { while (true) { if ($this->taskQueue->isEmpty()) { $this->ioPoll(null); } else { $this->ioPoll(0); } yield; } }
рдЗрд╕ рдХрд╛рд░реНрдп рдХреЛ рдХреБрдЫ рдмрд┐рдВрджреБ рдкрд░ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдЖрдк
$this->newTask($this->ioPollTask())
рдЬреЛрдбрд╝ рд╕рдХрддреЗ
$this->newTask($this->ioPollTask())
run()
рдХреЗ рд╢реАрд░реНрд╖
run()
рд╡рд┐рдзрд┐ рдХреЗ рд▓рд┐рдПред рдлрд┐рд░ рдпрд╣ рдХрд┐рд╕реА рднреА рдЕрдиреНрдп рдХрд╛рд░реНрдп рдХреА рддрд░рд╣ рдХрд╛рдо рдХрд░реЗрдЧрд╛, рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдмреАрдЪ рдкреНрд░рддреНрдпреЗрдХ рд╕реНрд╡рд┐рдЪрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЙрдкрд▓рдмреНрдз рддреИрдпрд╛рд░ рд╕реЙрдХреЗрдЯреНрд╕ рдХреА рдЬрд╛рдВрдЪ рдХрд░рдирд╛ред
ioPollTask
рдкрджреНрдзрддрд┐ рд╢реВрдиреНрдп рдЯрд╛рдЗрдордЖрдЙрдЯ рдХреЗ рд╕рд╛рде
ioPoll
рдХреЛ рдХреЙрд▓
ioPoll
, рдЗрд╕рд▓рд┐рдП stream_select рдмрд┐рдирд╛ рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд┐рдП рддреБрд░рдВрдд рдкрд░рд┐рдгрд╛рдо рд╡рд╛рдкрд╕ рдХрд░ рджреЗрдЧреАред
рдХреЗрд╡рд▓ рдЕрдЧрд░ рдХрд╛рд░реНрдп рдХрддрд╛рд░ рдЦрд╛рд▓реА рд╣реИ, рддреЛ рд╣рдо рд╕рдордп-рд╕реАрдорд╛ рдХреЗ рд░реВрдк рдореЗрдВ
null
рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ
stream_select
рдЗрдВрддрдЬрд╛рд░ рдХрд░реЗрдВрдЧреЗ рдЬрдм рддрдХ рдХрд┐ рд╕реЙрдХреЗрдЯреНрд╕ рдореЗрдВ рд╕реЗ рдХреЛрдИ рднреА рддреИрдпрд╛рд░ рди рд╣реЛред рдпрджрд┐ рд╣рдордиреЗ рдРрд╕рд╛ рдирд╣реАрдВ рдХрд┐рдпрд╛, рддреЛ рд╣рдо рдкреВрд░рд╛ рд╕реАрдкреАрдпреВ (рдХрдо рд╕реЗ рдХрдо рдХреЛрд░) рдЦрд╛рдПрдВрдЧреЗ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдХрд╛рд░реНрдп рдПрдХ рд▓реВрдк рдореЗрдВ рдмрд╛рд░-рдмрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ рдЬрдм рддрдХ рдХрд┐ рдХреЛрдИ рдЬреБрдбрд╝рд╛ рдирд╣реАрдВред
рд╕рд░реНрд╡рд░ рд╕реНрд╡рдпрдВ рдмрд╣реБрдд рд╕рд░рд▓ рджрд┐рдЦрддрд╛ рд╣реИ:
function server($port) { echo "Starting server at port $port...\n"; $socket = @stream_socket_server("tcp://localhost:$port", $errNo, $errStr); if (!$socket) throw new Exception($errStr, $errNo); stream_set_blocking($socket, 0); while (true) { yield waitForRead($socket); $clientSocket = stream_socket_accept($socket, 0); yield newTask(handleClient($clientSocket)); } } function handleClient($socket) { yield waitForRead($socket); $data = fread($socket, 8192); $msg = "Received following request:\n\n$data"; $msgLength = strlen($msg); $response = <<<RES HTTP/1.1 200 OK\r Content-Type: text/plain\r Content-Length: $msgLength\r Connection: close\r \r $msg RES; yield waitForWrite($socket); fwrite($socket, $response); fclose($socket); } $scheduler = new Scheduler; $scheduler->newTask(server(8000)); $scheduler->run();
рдпрд╣ рдкреЛрд░реНрдЯ 8000 рдкрд░ рдХрдиреЗрдХреНрд╢рди рд╕реНрд╡реАрдХрд╛рд░ рдХрд░реЗрдЧрд╛ рдФрд░ рдХреЗрд╡рд▓ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЕрдиреБрд░реЛрдз рдХреА рд╕рд╛рдордЧреНрд░реА рднреЗрдЬреЗрдЧрд╛ред рдХреБрдЫ "рд╡рд╛рд╕реНрддрд╡рд┐рдХ" рдХрд░рдирд╛ рд╕рд╣реА рд╣реЛрдЧрд╛ (HTTP рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рд╕рд╣реА рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдЗрд╕ рд▓реЗрдЦ рдХрд╛ рд╡рд┐рд╖рдп рдирд╣реАрдВ рд╣реИ)ред
рдЕрдм рдЖрдк рдЗрд╕ рд╕рд░реНрд╡рд░ рдХреЛ
ab -n 10000 -c 100 localhost:8000/
рдЬреИрд╕реА рдХрд┐рд╕реА рдЪреАрдЬ рдХреЗ рд╕рд╛рде рдЯреЗрд╕реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рддреЛ рд╣рдо 10000 рдЕрдиреБрд░реЛрдз рднреЗрдЬреЗрдВрдЧреЗ, рдЬрд┐рдирдореЗрдВ рд╕реЗ 100 рдЙрд╕реА рд╕рдордп рднреЗрдЬреЗ рдЬрд╛рдПрдВрдЧреЗред рдЗрд╕ рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдореБрдЭреЗ 10 рдорд┐рд▓реАрд╕реЗрдХрдВрдб рдХреА рдФрд╕рдд рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдорд┐рд▓реАред рд▓реЗрдХрд┐рди рдХреБрдЫ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рдорд╕реНрдпрд╛ рдереА рдЬрд┐рд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдореЗрдВ
рдмрд╣реБрдд рд╕рдордп рд▓рдЧрд╛ (5 рд╕реЗрдХрдВрдб рдореЗрдВ рдЬрд┐рд▓рд╛), рдЗрд╕рд▓рд┐рдП рдХреБрд▓ рдереНрд░реВрдкреБрдЯ рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб рдХреЗрд╡рд▓ 2000 рдЕрдиреБрд░реЛрдз рд╣реИрдВред рдЙрдЪреНрдЪ рдкреНрд░рддрд┐рдпреЛрдЧрд┐рддрд╛ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП,
-c 500
) рдХреЗ рд╕рд╛рде, рд╕реНрдХреНрд░рд┐рдкреНрдЯ рднреА рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдХрд╛рдо рдХрд░рддреА рд╣реИ, рд▓реЗрдХрд┐рди рдХреБрдЫ рдХрдиреЗрдХреНрд╢рди "рд╕рд╣рдХрд░реНрдореА рджреНрд╡рд╛рд░рд╛ рд░реАрд╕реЗрдЯ рд░реАрд╕реЗрдЯ" рддреНрд░реБрдЯрд┐ рдлреЗрдВрдХрддреЗ рд╣реИрдВред
рджреВрд░рд╕реНрде рдХреЛрд░рдЯрд╛рдЗрди
рдпрджрд┐ рдЖрдк рд╣рдорд╛рд░реЗ рдЕрдиреБрд╕реВрдЪрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдмрдбрд╝реА рдкреНрд░рдгрд╛рд▓реА рдмрдирд╛рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдЬрд▓реНрдж рд╣реА рдПрдХ рд╕рдорд╕реНрдпрд╛ рдореЗрдВ рдЖ рдЬрд╛рдПрдВрдЧреЗ: рд╣рдо рдЕрдХреНрд╕рд░ рдХреБрдЫ рдЯреБрдХрдбрд╝реЛрдВ рдХреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рд▓рдЧрд╛рдХрд░ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдХреЙрд▓ рдХрд░рдХреЗ рдХреЛрдб рдХреЛ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди coroutines рдХреЗ рд╕рд╛рде рдпрд╣ рд╕рдВрднрд╡ рдирд╣реАрдВ рд╣реИред рдЗрд╕ рдХреЛрдб рдХреА рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ:
function echoTimes($msg, $max) { for ($i = 1; $i <= $max; ++$i) { echo "$msg iteration $i\n"; yield; } } function task() { echoTimes('foo', 10); // print foo ten times echo "---\n"; echoTimes('bar', 5); // print bar five times yield; // force it to be a coroutine } $scheduler = new Scheduler; $scheduler->newTask(task()); $scheduler->run();
рдЗрд╕ рдХреЛрдб рдореЗрдВ рд╣рдордиреЗ
task()
рд╕реЗ рдХреЛрд░рдЯрд╛рдЗрди рдирд┐рдХрд╛рд▓рдиреЗ рдФрд░ рдЙрд╕реЗ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд┐рдпрд╛ред рд▓реЗрдХрд┐рди рд╡рд╣ рдХрд╛рдо рдирд╣реАрдВ рдХрд░реЗрдЧрд╛ред
рдЬреИрд╕рд╛ рдХрд┐ рд▓реЗрдЦ рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдХрд╣рд╛ рдЧрдпрд╛ рдерд╛, рдЬрдирд░реЗрдЯрд░ рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рдЗрд╕рдореЗрдВ рдХреЛрдИ рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдирд╣реАрдВ рд╣реЛрдЧрд╛, рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рдпрд╣ рдХреЗрд╡рд▓ рдПрдХ рдХреНрд▓рд╛рд╕ рдСрдмреНрдЬреЗрдХреНрдЯ рд╡рд╛рдкрд╕ рдХрд░реЗрдЧрд╛ Generator
ред рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдпрд╣реА рд╣реЛрддрд╛ рд╣реИ, рдХреЙрд▓ echoTimes()
рдХреБрдЫ рднреА рдирд╣реАрдВ рдХрд░реЗрдВрдЧреЗ, рд╡реЗ рдХреЗрд╡рд▓ рдСрдмреНрдЬреЗрдХреНрдЯ рд╡рд╛рдкрд╕ рдХрд░реЗрдВрдЧреЗредрдЗрд╕рдХреЗ рд▓рд┐рдП рдмрд╛рд╣рд░ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдХреЛрд░рдЖрдЙрдЯ рдХреЗ рд▓рд┐рдП рдПрдХ рдЫреЛрдЯрд╛ рдЖрд╡рд░рдг рд▓рд┐рдЦрдирд╛ рд╣реЛрдЧрд╛ред рдЗрд╕ рддрд░рд╣ рд╕реЗ рд╣рдо рдЙрдк-рдХреЙрд░рдЖрдЙрдЯ рдХреЛ рдХреЙрд▓ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ: $retval = (yield someCoroutine($foo, $bar));
рдЙрдк-рдХреЛрд░рдЖрдЙрдЯреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдорд╛рди рднреА рд▓реМрдЯрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ yield
: yield retval("I'm return value!");
рдлрд╝рдВрдХреНрд╢рди retval()
рдорд╛рди рдкрд░ рдПрдХ рдЖрд╡рд░рдг рдХреЛ рдЫреЛрдбрд╝рдХрд░ рдХреБрдЫ рднреА рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рд╣рдореЗрдВ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╡рд╛рдкрд╕реА рдореВрд▓реНрдп рд╣реИ: class CoroutineReturnValue { protected $value; public function __construct($value) { $this->value = $value; } public function getValue() { return $this->value; } } function retval($value) { return new CoroutineReturnValue($value); }
рдПрдХ рдирд┐рдпрдорд┐рдд рдХреЛрд░рдЖрдЙрдЯ рд╕реЗ рдПрдХ рдорд┐рд╢реНрд░рд┐рдд рдХреЛрд░рдЖрдЙрдЯ (рдЙрдк-рдХреЛрд░рдЖрдЙрдЯ рдХреЗ рд╕рд╛рде) рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдПрдХ рдФрд░ рдХрд╛рд░реНрдп рд▓рд┐рдЦрдирд╛ рд╣реЛрдЧрд╛ (рдЬреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдПрдХ рдФрд░ рдХреЛрд░рдЖрдЙрдЯ рд╣реИ): function stackedCoroutine(Generator $gen) { $stack = new SplStack; for (;;) { $value = $gen->current(); if ($value instanceof Generator) { $stack->push($gen); $gen = $value; continue; } $isReturnValue = $value instanceof CoroutineReturnValue; if (!$gen->valid() || $isReturnValue) { if ($stack->isEmpty()) { return; } $gen = $stack->pop(); $gen->send($isReturnValue ? $value->getValue() : NULL); continue; } $gen->send(yield $gen->key() => $value); } }
рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдХреЙрд▓рд░ рдФрд░ рдЪрд▓ рд░рд╣реЗ рдЙрдк-рдХреЛрд░рдЖрдЙрдЯ рдХреЗ рдмреАрдЪ рдПрдХ рд╕рд░рд▓ рдкреНрд░реЙрдХреНрд╕реА рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд░рддрд╛ рд╣реИред рдпрд╣ рднреА рдЬрд╛рдВрдЪрддрд╛ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рд╡рд╛рдкрд╕реА рдореВрд▓реНрдп рднреА рдПрдХ рдЬрдирд░реЗрдЯрд░ рд╣реИ рдФрд░ рдпрджрд┐ рд╣рд╛рдВ, рддреЛ рдЗрд╕реЗ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИред рдЬрдм рд╡рд╣ рд╡рд╕реНрддреБ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреА рд╣реИ CoroutineReturnValue
, рддреЛ рд╡рд╣ рдорд╛рддрд╛-рдкрд┐рддрд╛ рдХреЛ рдХреЛрд░рдЯрд╛рдЗрди рд▓реЗрдЧреА рдФрд░ рдЗрд╕рдХрд╛ рдирд┐рд╖реНрдкрд╛рджрди рдЬрд╛рд░реА рд░рдЦреЗрдЧреАредрддрд╛рдХрд┐ рдпреМрдЧрд┐рдХ рдХреЛрд░рдЯрд╛рдЗрди рдХреЛ рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗ, $this->coroutine = $coroutine;
рд╡рд░реНрдЧ рдирд┐рд░реНрдорд╛рдгрдХрд░реНрддрд╛ рдХреА рдкрдВрдХреНрддрд┐ рдХреЛ рдЗрд╕рдХреЗ Task
рд╕рд╛рде рдмрджрд▓рдирд╛ рд╣реЛрдЧрд╛$this->coroutine = stackedCoroutine($coroutine);
ред
рдЕрдм рд╣рдо рдкрдврд╝рдиреЗ, рд▓рд┐рдЦрдиреЗ рдФрд░ рдПрдХ рдирдпрд╛ рдХрдиреЗрдХреНрд╢рди рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд╕рд╛рде рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд╕рдореВрд╣реАрдХреГрдд рдХрд░рдХреЗ рдЕрдкрдиреЗ рд╡реЗрдм рд╕рд░реНрд╡рд░ рдХреЛ рдереЛрдбрд╝рд╛ рд╕реБрдзрд╛рд░ рд╕рдХрддреЗ рд╣реИрдВред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЗрд╕ рд╡рд░реНрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ: class CoSocket { protected $socket; public function __construct($socket) { $this->socket = $socket; } public function accept() { yield waitForRead($this->socket); yield retval(new CoSocket(stream_socket_accept($this->socket, 0))); } public function read($size) { yield waitForRead($this->socket); yield retval(fread($this->socket, $size)); } public function write($string) { yield waitForWrite($this->socket); fwrite($this->socket, $string); } public function close() { @fclose($this->socket); } }
рдЕрдм рд╕рд░реНрд╡рд░ рдХреЛ рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ: function server($port) { echo "Starting server at port $port...\n"; $socket = @stream_socket_server("tcp://localhost:$port", $errNo, $errStr); if (!$socket) throw new Exception($errStr, $errNo); stream_set_blocking($socket, 0); $socket = new CoSocket($socket); while (true) { yield newTask( handleClient(yield $socket->accept()) ); } } function handleClient($socket) { $data = (yield $socket->read(8192)); $msg = "Received following request:\n\n$data"; $msgLength = strlen($msg); $response = <<<RES HTTP/1.1 200 OK\r Content-Type: text/plain\r Content-Length: $msgLength\r Connection: close\r \r $msg RES; yield $socket->write($response); yield $socket->close(); }
рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдореЗрдВ рддреНрд░реБрдЯрд┐
рдПрдХ рдЕрдЪреНрдЫреЗ рдкреНрд░реЛрдЧреНрд░рд╛рдорд░ рдХреЗ рд░реВрдк рдореЗрдВ, рдЖрдкрдиреЗ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рджреЗрдЦрд╛ рд╣реИ рдХрд┐ рдЙрдкрд░реЛрдХреНрдд рдХреЛрдб рдЙрджрд╛рд╣рд░рдгреЛрдВ рдореЗрдВ рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдХреА рдХрдореА рд╣реИред рд╕реЙрдХреЗрдЯреНрд╕ рдХреЗ рд╕рд╛рде рдХреЛрдИ рднреА рдСрдкрд░реЗрд╢рди рд╣рдореЗрд╢рд╛ рддреНрд░реБрдЯрд┐ рдореЗрдВ рдкрдбрд╝ рд╕рдХрддрд╛ рд╣реИред рдореИрдВрдиреЗ рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП рдХрд┐рдпрд╛ рдХреНрдпреЛрдВрдХрд┐ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЗ рд▓рд┐рдП рддреНрд░реБрдЯрд┐ рд╕реЗ рдирд┐рдкрдЯрдирд╛ рдмрд╣реБрдд рдердХрд╛рдК рд╣реЛрддрд╛ рд╣реИ (рд╡рд┐рд╢реЗрд╖рдХрд░ рд╕реЙрдХреЗрдЯ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ) рдФрд░ рдпрд╣ рдХрдИ рдмрд╛рд░ рд╣рдорд╛рд░реЗ рдХреЛрдб рдХреЛ рдмрдврд╝рд╛рддрд╛ рд╣реИредрд▓реЗрдХрд┐рди, рдлрд┐рд░ рднреА, рдореИрдВ рд╕рд╛рдорд╛рдиреНрдп рд░реВрдк рд╕реЗ coroutines рдХреЗ рд▓рд┐рдП рддреНрд░реБрдЯрд┐ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛: coroutines рдЖрдкрдХреЛ рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЙрдирдХреЗ рдЕрдВрджрд░ рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХрдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ throw()
редрд╡рд┐рдзрд┐ throw()
рдкрд╣рд▓реЗ рддрд░реНрдХ рдореЗрдВ рдПрдХ рдЕрдкрд╡рд╛рдж рд▓реЗрддреА рд╣реИ рдФрд░ рдЗрд╕реЗ рдЙрд╕ рд╕реНрдерд╛рди рдкрд░ рдлреЗрдВрдХрддреА рд╣реИ рдЬрд╣рд╛рдВ рд╡рд░реНрддрдорд╛рди рдЙрдкрдЬ рд╣реИ (рдЬрд┐рд╕рдХрд╛ рдореВрд▓реНрдп рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ current()
): function gen() { echo "Foo\n"; try { yield; } catch (Exception $e) { echo "Exception: {$e->getMessage()}\n"; } echo "Bar\n"; } $gen = gen(); $gen->rewind(); // echos "Foo" $gen->throw(new Exception('Test')); // echos "Exception: Test" // and "Bar"
рдпрд╣ рдПрдХ рдмрд╣реБрдд рдЕрдЪреНрдЫреА рдмрд╛рдд рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╣рдо рдЕрдкрд╡рд╛рджреЛрдВ рдХреЛ рдлреЗрдВрдХрдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд┐рд╕реНрдХреЛрд▓ рдФрд░ рдЙрдк-рдХреЙрд░рдЯрд╛рдЗрди рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЕрд╡рд╕рд░ рджреЗ рд╕рдХрддреЗ рд╣реИрдВред рд╕рд┐рд╕реНрдХреЛрд▓ рдХреЗ рд▓рд┐рдП, рд╡рд┐рдзрд┐ Scheduler::run()
рдХреЛ рдереЛрдбрд╝рд╛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ: if ($retval instanceof SystemCall) { try { $retval($task, $this); } catch (Exception $e) { $task->setException($e); $this->schedule($task); } continue; }
рдФрд░ рдХрдХреНрд╖рд╛ Task
рдХреЛ рдХреЙрд▓ рд╕рдВрднрд╛рд▓рдиреА рдЪрд╛рд╣рд┐рдП throw()
: class Task { // ... protected $exception = null; public function setException($exception) { $this->exception = $exception; } public function run() { if ($this->beforeFirstYield) { $this->beforeFirstYield = false; return $this->coroutine->current(); } elseif ($this->exception) { $retval = $this->coroutine->throw($this->exception); $this->exception = null; return $retval; } else { $retval = $this->coroutine->send($this->sendValue); $this->sendValue = null; return $retval; } } // ... }
рдЕрдм рд╣рдо рд╕рд┐рд╕реНрдХреЛрд▓ рд╕реЗ рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХрдирд╛ рд╢реБрд░реВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ! рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, killTask
рдпрджрд┐ рдПрдХ рдЯреНрд░рд╛рдВрд╕рдорд┐рдЯреЗрдб рдЯрд╛рд╕реНрдХ рдЖрдЗрдбреЗрдВрдЯрд┐рдлрд╝рд╛рдпрд░ рдЕрдорд╛рдиреНрдп рд╣реИ, рддреЛ рдЗрд╕рдХрд╛ рдЕрдкрд╡рд╛рдж рдЫреЛрдбрд╝ рджреЗрдВ: function killTask($tid) { return new SystemCall( function(Task $task, Scheduler $scheduler) use ($tid) { if ($scheduler->killTask($tid)) { $scheduler->schedule($task); } else { throw new InvalidArgumentException('Invalid task ID!'); } } ); }
рдЕрдм рдЪрд▓реЛ рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реИрдВ: function task() { try { yield killTask(500); } catch (Exception $e) { echo 'Tried to kill task 500 but failed: ', $e->getMessage(), "\n"; } }
рдЕрдм рддрдХ рдпрд╣ рдХрд╛рдо рдирд╣реАрдВ рдХрд░реЗрдЧрд╛, рдЬреИрд╕рд╛ рдХрд┐ рдлрд╝рдВрдХреНрд╢рди stackedCoroutine
рдЕрдкрд╡рд╛рджреЛрдВ рдХреЛ рдирд╣реАрдВ рд╕рдВрднрд╛рд▓рддрд╛ рд╣реИред рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЗрд╕реЗ рдереЛрдбрд╝рд╛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдВ: function stackedCoroutine(Generator $gen) { $stack = new SplStack; $exception = null; for (;;) { try { if ($exception) { $gen->throw($exception); $exception = null; continue; } $value = $gen->current(); if ($value instanceof Generator) { $stack->push($gen); $gen = $value; continue; } $isReturnValue = $value instanceof CoroutineReturnValue; if (!$gen->valid() || $isReturnValue) { if ($stack->isEmpty()) { return; } $gen = $stack->pop(); $gen->send($isReturnValue ? $value->getValue() : NULL); continue; } try { $sendValue = (yield $gen->key() => $value); } catch (Exception $e) { $gen->throw($e); continue; } $gen->send($sendValue); } catch (Exception $e) { if ($stack->isEmpty()) { throw $e; } $gen = $stack->pop(); $exception = $e; } } }
рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рджреЗрдирд╛
рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рд╣рдордиреЗ рд╕рдВрдпреБрдХреНрдд рдорд▓реНрдЯреАрдЯрд╛рд╕реНрдХрд┐рдВрдЧ, рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рднреЗрдЬрдиреЗ рдХреА рдХреНрд╖рдорддрд╛, рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз рд╕рдВрдЪрд╛рд▓рди рдФрд░ рддреНрд░реБрдЯрд┐ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╣реБрдП рдПрдХ рдХрд╛рд░реНрдп рдЕрдиреБрд╕реВрдЪрдХ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд┐рдпрд╛ред рдЗрд╕ рд╕рдм рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫреА рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдЕрдВрддрд┐рдо рдХреЛрдб рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕рд┐рдВрдХреНрд░реЛрдирд╕ рджрд┐рдЦрддрд╛ рд╣реИ, рднрд▓реЗ рд╣реА рдпрд╣ рдХрдИ рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рд╕рдВрдЪрд╛рд▓рди рдХрд░рддрд╛ рд╣реИред рдпрджрд┐ рдЖрдкрдХреЛ рд╕реЙрдХреЗрдЯ рд╕реЗ рдбреЗрдЯрд╛ рдкрдврд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рдХреЙрд▓рдмреИрдХ рднреЗрдЬрдиреЗ рдпрд╛ рд╢реНрд░реЛрддрд╛ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рдЖрдк рд▓рд┐рдЦрддреЗ рд╣реИрдВ yield $socket->read()
редрдЬрдм рдореИрдВрдиреЗ рдкрд╣рд▓реА рдмрд╛рд░ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реБрдирд╛, рддреЛ рдореБрдЭреЗ рдПрд╣рд╕рд╛рд╕ рд╣реБрдЖ рдХрд┐ рдпрд╣ рдХрд┐рддрдирд╛ рдЕрдЪреНрдЫрд╛ рд╣реИ, рдФрд░ рдЗрд╕рдиреЗ рдореБрдЭреЗ рдЙрдиреНрд╣реЗрдВ PHP рдореЗрдВ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░реЗрд░рд┐рдд рдХрд┐рдпрд╛ред рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ, рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдбрд░рд╛рд╡рдиреЗ рд╣реИрдВред рд╢рд╛рдВрдд рдХреЛрдб рдФрд░ рдЦреМрдлрдирд╛рдХ рдЧрдВрджрдЧреА рдХреЗ рдмреАрдЪ рдПрдХ рдорд╣реАрди рд░реЗрдЦрд╛ рд╣реИ, рдФрд░ рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЙрд╕ рд▓рд╛рдЗрди рдкрд░ рдХреЛрд░рдЯрд╛рдЗрди рд╣реИрдВред рдореЗрд░реЗ рд▓рд┐рдП рдпрд╣ рдХрд╣рдирд╛ рдХрдард┐рди рд╣реИ рдХрд┐ рдХреНрдпрд╛ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдХреЛрд░рдЯрд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛, рдЬреИрд╕рд╛ рдХрд┐ рдореИрдВрдиреЗ рдКрдкрд░ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рд╣реИ, рдХрд┐рд╕реА рддрд░рд╣ рд╕реЗ рдлрд╛рдпрджреЗрдордВрдж рд╣реИредрдХрд┐рд╕реА рднреА рдорд╛рдорд▓реЗ рдореЗрдВ, рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдмрд╣реБрдд рд╣реА рджрд┐рд▓рдЪрд╕реНрдк рд╡рд┐рд╖рдп рд╣реИ, рдФрд░ рдореБрдЭреЗ рдЖрд╢рд╛ рд╣реИ рдХрд┐ рдЖрдк рднреА :)рдЕрдиреБрд╡рд╛рджрдХ рд╕реЗ рдкреБрдирд╢реНрдЪ: рд▓реЗрдЦ рдмрд╣реБрдд рдмрдбрд╝рд╛ рд╣реИ рдФрд░ рд╕рдордЭрдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрд╕рдореЗрдВ рддреНрд░реБрдЯрд┐рдпрд╛рдВ рд╣реЛ рд╕рдХрддреА рд╣реИрдВ, рдЬрд┐рдирдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ рдЕрд░реНрдеред рдЕрдЧрд░ рдЖрдкрдиреЗ рдпрд╣ рджреЗрдЦрд╛ - рдХреГрдкрдпрд╛ рднреЗрдЬреЗрдВред