Join 4,000+ others and never miss out on new tips, tutorials, and more.
Latest version:
pecl install openswoole-22.1.2 | composer require openswoole/core:22.1.5
Coroutines should only be executed within a Coroutine Context, also known as a Coroutine Container.
The function co::run()
creates a context which you can then execute coroutines inside of. A Coroutine context is also created for you at the Server callback functions, automatically, so you don't have to do this every time. This is only true if you have Coroutines enabled at the server level, refer to enable_coroutine
.
The co::run()
function is designed to replace the go() + OpenSwoole\Event::wait()
patten as a context for executing coroutines. A Coroutine context is required because it enables them to run inside of whats known as a Coroutine Scheduler, this is what allows Coroutines to automatically switch context for you when I/O is blocking a process, the Coroutine Scheduler can switch to another Coroutine within its context, without co::run()
switching becomes a manually task and can be very complex.
There are many ways to have a coroutine context within the ecosystem of OpenSwoole:
co::run()
you can use coroutines by themselves<?php
use OpenSwoole\Coroutine as Co;
co::run(function()
{
go(function()
{
Co::sleep(1);
echo "Coroutine 1 is done.\n";
});
go(function()
{
Co::sleep(1);
echo "Coroutine 2 is done.\n";
});
});
echo "Outside any Coroutine Context.\n";
To test the code above for yourself, simply place the code in a file like coroutines.php
and run from the command line php coroutine.php
. The function co::run()
creates the coroutine context and can be thought of like Java or C main
function.
The co::run()
function is part of the OpenSwoole\Coroutine
namespace but to make development easier it is also provided as a function using the namespace: use function OpenSwoole\co::run
.
The go()
function is also part of the OpenSwoole\Coroutine
namespace but is defined as a function to make development easier. So you can reference use function OpenSwoole\Coroutine\go
as well.
Keep in mind that the
run
inco::run
is lowercase
You can concurrently execute two things at once with OpenSwoole Coroutines:
<?php
use OpenSwoole\Coroutine as Co;
co::run(function()
{
go(function()
{
// Runs immediately
var_dump(file_get_contents("http://openswoole.com/"));
});
go(function()
{
// Waits once second before continuing
Co::sleep(1);
var_dump(file_get_contents("http://www.google.com/"));
});
});
echo "Outside any Coroutine Context.\n";
It is not possible to nest co::run
because you can only create one context per set of Coroutines but you may create multiple, separate co::run
context containers on their own.
If you try to nest co::run
you will get an error something similar to:
PHP Warning: OpenSwoole\Coroutine\Scheduler::start(): eventLoop has already been created. unable to start OpenSwoole\Coroutine\Scheduler in @swoole-src/library/core/Coroutine/functions.php on line 24
The event-loop has already been started in the initial co::run
so starting another will cause conflict and is not allowed. Instead you need to create multiple co::run
containers separately like so:
<?php
// Context 1
co::run(function()
{
...
});
// Context 2
co::run(function()
{
...
});
...
Note: Even though the above code will work, co::run
will block until all the events within the context have executed, the context must finish first before code execution continues. To solve this limitation, we can use the Coroutine Scheduler.
The function co::run
is actually just a wrapper for the OpenSwoole\Coroutine\Scheduler
class. Still part of the main OpenSwoole\Coroutine
namespace but co::run
is just an easy and quick way to create one coroutine context which executes at a time.
For more advanced usage, you can use the Coroutine Scheduler which allows you creates multiple coroutine context containers which all run at once and do not block each other.
<?php
// Multiple Coroutine Context Containers at once
$run = new OpenSwoole\Coroutine\Scheduler;
// Context 1
$run->add(function()
{
Co::sleep(1);
echo "Context 1 is done.\n";
});
// Context 2
$run->add(function()
{
Co::sleep(1);
echo "Context 2 is done.\n";
});
// Context 3
$run->add(function()
{
echo "Context 3 is done.\n";
});
// Required or context containers won't run
$run->start();
// Or one context at a time
co::run(function()
{
Co::sleep(1);
echo "co::run context is done.\n";
});
With the Coroutine Scheduler example you will see the output of:
Context 3 is done.
Context 1 is done.
Context 2 is done.
This is because we have used the Coroutine Scheduler to start multiple context containers which run under a single event-loop and are able to run simultaneously. Whereas out single co::run` example can only run one after another.
The Coroutine Scheduler is actually a low-level API, giving you access to OpenSwoole at a low level, this should only really be used for advanced use cases.
The Coroutine Scheduler is a low-level API to OpenSwoole, most of the time you should not need to use the Coroutine Scheduler yourself, it is managed for you but the API is provided to you.
<?php
OpenSwoole\Coroutine\Scheduler->set(array $options): bool
Set the runtime parameters of the coroutine scheduler, related to Coroutine options only. Refer to the specific Coroutine options for details.
This method is actually just an alias of OpenSwoole\Coroutine::set()
.
Example
<?php
$scheduler = new OpenSwoole\Coroutine\Scheduler;
$scheduler->set(['max_coroutine' => 100]);
<?php
OpenSwoole\Coroutine\Scheduler->getOptions(): null|array
Get the set runtime parameters of the coroutine scheduler, these settings are set using set()
.
This method is an alias of OpenSwoole\Coroutine::getOptions()
. Refer to the coroutine documentation.
<?php
OpenSwoole\Coroutine\Scheduler->add(callable $fn, ... $args): bool
Add new Coroutine Context to the Scheduler. These can be thought of as tasks to run, they only get executed once you call $Scheduler->start()
. This is a main method when using the Scheduler as it allows you to add multiple context containers at a time.
$fn
The callback function to execute when the Coroutine Context starts
$args
You may use this parameter to pass optional arguments to the specified function
Example
<?php
use OpenSwoole\Coroutine;
$scheduler = new Coroutine\Scheduler;
// Context 1
$scheduler->add(function($a, $b)
{
Coroutine::sleep(1);
echo assert($a == 'Hello ') . PHP_EOL;
echo assert($b == 'World!') . PHP_EOL;
echo "Context with assert has completed.\n";
}, 'Hello ', 'World!');
// Context 2
$scheduler->add(function()
{
echo "I will finish first.\n";
});
// Blocks until all tasks complete
$scheduler->start();
The above code will show:
I will finish first.
1
1
Context with assert has completed.
Once you call $scheduler->start()
the Scheduler will start each context and execute them at the same time, the first context waits one second, allowing you to see that context 2 runs immediately and is not blocked by the context 1 sleep call.
<?php
OpenSwoole\Coroutine\Scheduler->parallel(int $num, callable $fn, ... $args): bool
Add new Coroutine Context to the Schedule but execute a number of parallel tasks using Coroutines. This method will start Coroutines simultaneously when the Scheduler begins and execute Coroutines in parallel.
$num
The number of coroutines to start in parallel.
$fn
The callable function to use when starting parallel tasks/coroutines.
$args
Optional function arguments to pass to each new coroutine that is started with the $fn
callable.
Example
<?php
use OpenSwoole\Coroutine;
$scheduler = new Coroutine\Scheduler;
$scheduler->parallel(10, function($time, $msg)
{
Coroutine::sleep($time);
echo $msg . " from co " . Coroutine::getCid() . "\n";
}, 0.05, 'Hello World!');
// Blocks until all tasks complete
$scheduler->start();
Order of execution when started is not guaranteed
<?php
OpenSwoole\Coroutine\Scheduler->start(): bool
Start the Coroutine Scheduler, begin any context containers which were created from add()
or parallel()
. If you do not call this method, no context containers or Coroutines will execute.
This method will return true
if there were no errors and a successful start in adding each task, in the case of any problems, it will return false
in regard to adding any new tasks. You may get errors if a Scheduler has already been started/created and cannot begin a new one.