Comparing Python Coroutines and Go Goroutines: Concepts, Differences, and Practical Usage
This article explains what coroutines are, compares Python's coroutine implementation with Go's goroutine model, discusses their execution characteristics, and provides code examples illustrating producer‑consumer pipelines and async/await usage in both languages.
What is a coroutine
Before discussing the differences between Python and Go, we first define what a coroutine is. A coroutine can be understood as a special kind of program call that can be interrupted inside a sub‑routine (or function) and later resumed at the same point.
Interruptible : the interruption is not a normal function call but similar to a CPU interrupt, allowing the CPU to switch to another task.
Resumable : at an appropriate moment the execution can continue from the point of interruption.
Difference from processes and threads
These two characteristics give coroutines a much higher execution efficiency compared with thread or process switching. A process is the OS's resource allocation unit, while a thread is the smallest unit the OS schedules for execution. Coroutines, often called "micro‑threads", are not on the same dimension as processes or threads; their switching happens entirely in user space without kernel involvement.
Python coroutines (coroutine)
Python's coroutines are essentially the same as Go's goroutine in concept: they can be interrupted and resumed at developer‑controlled points. Because of the GIL, coroutine switching is only useful for I/O‑bound code, not CPU‑bound code.
Implementation-wise, a coroutine can be seen as a generator + scheduling strategy . The yield keyword causes the generator to pause, and a scheduler drives the coroutine's execution and resumption. Simple scheduling can be a busy loop, while more complex strategies use an event loop such as epoll in asyncio or tornado.
Example of a simple producer‑consumer coroutine:
import time
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
time.sleep(1)
r = '200 OK'
def produce(c):
c.next()
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
if __name__ == '__main__':
c = consumer()
produce(c)This demonstrates a traditional producer‑consumer model where the consumer function is a coroutine (generator) that yields control back to the produce function, which drives its resumption.
In backend services, Python coroutines are typically used within asynchronous I/O frameworks (e.g., asyncio, tornado) to handle I/O‑intensive workloads efficiently.
Native async/await coroutine example:
def main():
define_options()
options.parse_command_line()
# Use uvloop instead of the default event loop
# asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
app = tornado.web.Application(handlers=handlers, debug=options.debug)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
asyncio.get_event_loop().run_forever()Async/await handler example:
class RcOutputHandler(BaseHandler):
async def post(self):
status, msg, user = self.check_args('uid', 'order_no', 'mid', 'phone', 'name', 'apply_id', 'product_id')
if status != ErrorCodeConfig.SUCCESS:
status, msg, report = status, msg, None
else:
rcoutput_flow_instance = ZHANRONG_CUSTOM_PRODUCTID_RCFLOW_MAP.get(user.product_id, RcOutputFlowControler())
status, msg, report = await rcoutput_flow_instance.get_rcoutput_result(user)
res = self.generate_response_data(status, msg, report)
await self.finish(res)
# Accompany process
await AccompanyRunningFlowControler().get_accompany_data(user)Python coroutine characteristics
Single‑threaded switching, suitable for I/O‑bound programs to maximize I/O multiplexing.
Cannot utilize multiple CPU cores.
Coroutines run sequentially, so no data‑race concerns.
Versatile usage: web services, pipeline data processing, etc.
Go coroutines (goroutine)
Go's goroutine combines coroutine and thread advantages, providing built‑in concurrency support. Starting a goroutine is as simple as prefixing a function call with go.
Like Python coroutines, goroutines can be interrupted and resumed when a channel read/write blocks or a system call occurs.
Goroutine implementation runs on multiple cores, enabling parallel execution. The Go scheduler uses the M‑P‑G model (Machine‑Processor‑Goroutine).
In the M‑P‑G model: M (Machine) is a kernel thread managed by the OS. P (Processor) holds the execution context needed by M to run goroutines. G (Goroutine) is a lightweight thread with its own stack and scheduling information.
The scheduler creates G objects, assigns them to local or global queues, creates M threads as needed, and executes G tasks according to availability of P processors.
Go coroutine characteristics
Coroutines must ensure data safety via channels or locks.
Can leverage multiple cores for parallel execution.
Coroutines may run concurrently depending on channel design.
Pre‑emptive scheduling may lead to unfairness.
Differences between Python coroutine and Go goroutine
Both are interruptible and resumable, but goroutines can run in parallel on multiple cores, while Python coroutines are always sequential within a single thread. Consequently, coroutines are best for I/O‑bound workloads, whereas goroutines perform well for both I/O‑ and CPU‑bound tasks.
From a runtime perspective, coroutines use cooperative multitasking, requiring the program to voluntarily yield control, whereas goroutines use pre‑emptive multitasking similar to traditional threads.
Mapping to thread models:
N:1 – Python coroutines: many coroutines share one thread (high I/O efficiency, no multi‑core usage).
1:1 – Java threads: each coroutine runs in its own thread (high overhead).
1:1 – Go model: many goroutines run on multiple threads, allowing both multi‑core parallelism and low‑overhead switching.
Communication and scheduling differences are illustrated in the following diagram:
Original source: https://segmentfault.com/a/1190000038241863
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
