Concurrency in Python
In Python, early concurrency methods were dominated by traditional multi-processing and multi-threading, similar to Java. At the same time, there were many third-party asynchronous solutions (gevent/tornado/twisted, etc.).
In the Python 3 era, the official asyncio library and async/await syntax were introduced as Python’s official coroutine implementation, which gradually became popular.
Processes
Example of multi-processing programming:
from multiprocessing import Process
def f(name):
print('hello', name)
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()
The API of multiprocessing is close to threading, making it relatively easy to create multi-process programs. It is a solution recommended by Python officially to bypass the GIL restriction of multi-threading.
However, it should be noted that the parameters for creating a process need to be pickle-serializable. It is best to use process-safe data structures like Pipe and Queue (Official Programming guidelines).
Threads
Example of multi-threading code:
from threading import Thread
def f(name):
print('hello', name)
if __name__ == '__main__':
p = Thread(target=f, args=('bob',))
p.start()
p.join()
# Thread pool approach
with ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(pow, 323, 1235)
print(future.result())
Drawback of CPython Threads: GIL (Global Interpreter Lock)
The GIL is a global lock used by CPython when executing Python bytecodes, preventing the interpreter from fully utilizing multi-cores for CPU-bound tasks, while IO-bound tasks release the GIL.
To bypass the GIL, one must switch to multi-processing or use C extensions.
Coroutines
asyncio example:
import asyncio
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"started at {time.strftime('%X')}")
await asyncio.gather(say_after(1, 'hello'), say_after(2,'world'))
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
# started at 22:32:23
# hello
# world
# finished at 22:32:25
async syntax and asyncio
Starting from version 3.4, Python’s standard library includes the asyncio module, and from 3.5 onwards, it supports async/await syntax.
The implementation of Python coroutines can be traced back to the yield keyword and the generator structure introduced in the Python 2 era:
- Generators pause via
yieldand can return values. - The caller resumes the generator via
next()orsend()methods, and can send data to the generator viasend(). - The
yield fromsyntactic sugar facilitates iterating over every value in a generator. - With the introduction of
async/awaitsyntax, the coroutine type was officially established. - The
asynciolibrary provides an official event loop implementation and supports IO multiplexing on different operating systems (select/epoll/iocp, etc.), or can be replaced by third-party implementations (like uvloop) via configuration. - With the help of the
concurrent.futuresthread pool/process pool module, it supports multi-threading/multi-processing, but the event loop itself remains single-threaded.
Concurrency in Go
goroutine and channel example:
package main
import "fmt"
func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
goroutine and channel
Golang implements user-space coroutines called goroutines, scheduling them via the GPM model.

It also supports network IO multiplexing via netpoller.
Communication between different goroutines is achieved through channels.
CSP
CSP (Communicating Sequential Processes) is a concurrency model that interacts via message passing rather than shared variables.