Fundamentals 16 min read

The Evolution and Principles of Coroutines: From COBOL to Modern Languages

This article traces the historical origins of coroutines from early COBOL compiler challenges, explains their cooperative execution model, compares them with thread scheduling, and reviews how modern languages like Python, C++ and Go implement coroutine support to improve IO‑bound performance.

Refining Core Development Skills
Refining Core Development Skills
Refining Core Development Skills
The Evolution and Principles of Coroutines: From COBOL to Modern Languages

The article begins with a personal motivation to clarify the vague understanding of coroutines after years of backend development in C/C++, Python, and Go.

It explains that a coroutine is a broad design concept whose concrete implementations vary, and that the idea predates threads, dating back to the 1950s with the COBOL language.

Historical context is provided: early COBOL compilers in the late 1950s faced severe hardware constraints, such as tape‑based storage that prevented one‑pass compilation, leading to multi‑pass designs where lexical and syntactic analyses had to rewind the tape repeatedly.

Melvin Conway’s cooperative solution interleaved lexical and syntactic analysis, allowing the compiler to yield control between the two phases while preserving state—essentially the earliest coroutine concept.

The article discusses why coroutines remained obscure, noting their incompatibility with the dominant top‑down design philosophy and the dominance of preemptive threads, which offered broader applicability despite higher overhead.

It then describes the resurgence of coroutines as a response to the inefficiency of preemptive scheduling for IO‑bound tasks, highlighting the advantages of user‑mode cooperative scheduling (yield/resume) for reducing context‑switch costs.

Limitations of coroutines are listed, such as lack of native multi‑core utilization, potential starvation, and security concerns when user‑controlled scheduling replaces OS‑managed preemption.

The article surveys language support: Python’s evolution from generator‑based yields to async/await (with a code example), C++20’s low‑level coroutine framework used by libraries like boost::coroutine, and Go’s native goroutine model built on the GMP (G‑M‑P) scheduler.

import asyncio
from pathlib import Path
import logging
from urllib.request import urlopen, Request
import os
from time import time
import aiohttp

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

CODEFLEX_IMAGES_URLS = [
    'https://codeflex.co/wp-content/uploads/2021/01/pandas-dataframe-python-1024x512.png',
    'https://codeflex.co/wp-content/uploads/2021/02/github-actions-deployment-to-eks-with-kustomize-1024x536.jpg',
    'https://codeflex.co/wp-content/uploads/2021/02/boto3-s3-multipart-upload-1024x536.jpg',
    'https://codeflex.co/wp-content/uploads/2018/02/kafka-cluster-architecture.jpg',
    'https://codeflex.co/wp-content/uploads/2016/09/redis-cluster-topology.png'
]

async def download_image_async(session, dir, img_url):
    download_path = dir / os.path.basename(img_url)
    async with session.get(img_url) as response:
        with download_path.open('wb') as f:
            while True:
                chunk = await response.content.read(512)
                if not chunk:
                    break
                f.write(chunk)
    logger.info('Downloaded: ' + img_url)

async def main():
    images_dir = Path('codeflex_images')
    Path('codeflex_images').mkdir(parents=False, exist_ok=True)
    async with aiohttp.ClientSession() as session:
        tasks = [download_image_async(session, images_dir, img_url) for img_url in CODEFLEX_IMAGES_URLS]
        await asyncio.gather(*tasks, return_exceptions=True)

if __name__ == '__main__':
    start = time()
    event_loop = asyncio.get_event_loop()
    try:
        event_loop.run_until_complete(main())
    finally:
        event_loop.close()
    logger.info('Download time: %s seconds', time() - start)

Finally, the article concludes that coroutines complement, rather than replace, threads by excelling at IO‑bound workloads, and encourages developers to adopt coroutine‑friendly designs where appropriate.

PythonConcurrencyGoCcoroutineio
Refining Core Development Skills
Written by

Refining Core Development Skills

Fei has over 10 years of development experience at Tencent and Sogou. Through this account, he shares his deep insights on performance.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.