First of all you have to understand:
- the difference between a coroutine and a thread, and
- the difference between concurrency and parallelism
The TL;DR is that threads are managed by the OS whereas coroutines are managed by the language. OS does not know what a coroutine is. OS preemptively interrupts threads and interleaves them (this is known as concurrent execution) to give the illusion of parallelism. It is an illusion because concurrency is not the same as parallelism . Coroutines run within a thread and are managed entirely by the language.
Just as the OS interleaves threads, a single thread interleaves coroutines running within it. The difference is that whereas threads are preemptively interrupted, coroutines have well-defined points where execution is yielded to another coroutine.
The OS might be running say 30 threads to take an example. Within each thread you may further have 30 coroutines running.
Understand pre-emptive (this is what threads do; a thread has no control over when it will be interrupted; the OS can interrupt it suddenly at any unpredictable point) vs. co-operative (this is what coroutines do) multitasking:
The term preemptive multitasking is used to distinguish a multitasking operating system, which permits preemption of tasks, from a cooperative multitasking system wherein processes or tasks must be explicitly programmed to yield when they do not need system resources.
In simple terms: Preemptive multitasking involves the use of an interrupt mechanism which suspends the currently executing process and invokes a scheduler to determine which process should execute next. Therefore, all processes will get some amount of CPU time at any given time
Read this as this is the essence of Swoole: https://www.swoole.co.uk/docs/modules/swoole-coroutine
A coroutine can be simply understood as a thread, but this thread is in user mode and does not require the participation of the operating system. The cost of creating, destroying and switching is very low. Unlike threads, the coroutine cannot use multiple CPU core because it operates in user space
Swoole creates one coroutine for each request to the server and switches coroutines based on I/O status automatically, this happens within the coroutine scheduler.
So the beauty is that you write code as if you were programming synchronously but Swoole will automatically switch coroutines based on I/O status thus giving you benefits that come with async execution.
A coroutine context is created using
Co\run. The context is created automatically for you for each HTTP request.
A coroutine context is created with the callback function: request, receive, connect in a Swoole\Server or Swoole\HTTP\Server for you, so you can start using coroutines straight away within those callbacks.
A coroutine can then be created within the context using the
inside each go call is a coroutine
Coroutines have access to the same memory space, they can conflict with modifying memory which could be dependent by another coroutine as they are running within user space, on the same thread.
To solve the problem of conflicting memory access we have channels , they are used for the communication between coroutines.
$chan->pop(); // Read data from the channel, it will block and wait if the channel is empty $chan->push(); // Write data into the channel, it will block and wait if the channel is full
go, Create a new coroutine.
go()is alias of
chan, Create a new channel for message-passing
defer, Delay the task until the exit of the coroutine, FIFO
Swoole coroutines are analogous to goroutines in Go with some differences .
More notes from: http://vesko.blogs.azonmedia.com/2019/09/19/coroutines-in-swoole/ (an excellent article)
Each coroutine has its own stack but shares the global state of the PHP process (the Worker) with the rest of the coroutines.
The parent/child relationship of the coroutines does not mean that that they are nested like the function calls/stack. Unlike the functions the child coroutine can end after the parent one and both coroutines are running in parallel (in terms that they can get and yield execution multiple times in between giving the impression of parallel execution, not that they are actually running in parallel as it is explained further down).
The coroutines yield the control to the scheduler only at certain points. In Swoole these are requests that can be served asynchronously like database queries, file operations, etc. By saying asynchronous I do not mean that these are using callbacks, but instead that at the point of a database query being sent the coroutine gives the control back to the scheduler to run another coroutine. The first coroutine will be resumed once the data is received from the DB and the currently running coroutine gives up the control.
It is very important to note that the core PHP libraries and DB extensions are blocking and do not allow for coroutine switching. This means that if you execute mysqli_connect() the Swoole Worker will wait (block) for the result of the operations instead of yielding the control to another coroutine. Thus running an existing PHP code in Swoole will make no use at all of the coroutines.
On the contrary – the global shared state allows for a pool of database connections to be created and connections from this pool to be obtained by the coroutines as needed and then released. Because of the persistent nature of Swoole, these connections do not have to be opened and closed for each request but instead once they are opened when the Swoole Server is started these only need to be pinged to keep them alive. The connection pool provides a speedup compared to the traditional setup as it saves time for opening the DB connection.
Another good read: http://swoft.io/docs/2.x/en/ready/swoole.html
The essence of Swoole is its coroutines which are very similar to Go. But how do the Swoole coroutines work internally? They work by tapping into the epoll I/O event notification facility of Linux. This is how all the magic happens ref . And this is also the reason why Swoole does not work on Windows since
epoll is specific to Linux.
- Xdebug must be disabled when using Swoole and Yasd does not work. So debugging is going to be harder.
- Also can’t use xhprof, Blackfire ref . Also can’t use
- Can’t just edit and save php files for changes to take effect like you can do with PHP-FPM. Have to restart the server (just like you have to do with Node.js) or send
USRsignal to worker process.