Python 并发编程核心概念详解

3 阅读1小时+

目录

第一部分:基础概念篇

  • 一、核心概念定义
    • 1.1 并发(Concurrency)
    • 1.2 并行(Parallelism)
    • 1.3 进程(Process)
    • 1.4 线程(Thread)
    • 1.5 协程(Coroutine)
    • 1.6 CPU密集型任务
    • 1.7 IO密集型任务

第二部分:对比分析篇

第三部分:底层原理篇

第四部分:实战应用篇


一、核心概念定义

1.1 并发(Concurrency)

定义: 并发是指系统能够处理多个任务的能力,这些任务在时间上可以交替执行,给人一种"同时"执行的感觉。

本质: 一个CPU在多个任务之间快速切换

类比:

  • 一个厨师在做三道菜:炒菜A、煮汤B、蒸饭C
  • 厨师先炒菜A一会儿,然后去看汤B,再去检查饭C
  • 虽然同一时刻只做一件事,但通过快速切换,三道菜都在"同时"进行
import time
import threading

def task(name, duration):
    """模拟并发任务"""
    print(f"[{time.strftime('%H:%M:%S')}] {name} 开始")
    time.sleep(duration)
    print(f"[{time.strftime('%H:%M:%S')}] {name} 完成")

# 并发执行
threads = []
for i in range(3):
    t = threading.Thread(target=task, args=(f"任务{i+1}", 2))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

# 输出示例(交替执行):
# [14:30:00] 任务1 开始
# [14:30:00] 任务2 开始
# [14:30:00] 任务3 开始
# [14:30:02] 任务1 完成
# [14:30:02] 任务2 完成
# [14:30:02] 任务3 完成

关键特征:

  • ✅ 任务可以交替执行
  • ✅ 不需要多核CPU
  • ✅ 适合I/O密集型任务
  • ❌ 同一时刻只有一个任务真正在执行

1.2 并行(Parallelism)

定义: 并行是指多个任务在同一时刻真正同时执行,需要多核CPU或多个处理器。

本质: 多个CPU同时执行多个任务

类比:

  • 三个厨师同时做三道菜
  • 每个厨师专注于自己的菜,真正的同时进行
  • 效率是单个厨师的三倍
import time
from multiprocessing import Process, cpu_count

def cpu_task(name, n):
    """CPU密集型任务"""
    print(f"[{time.strftime('%H:%M:%S')}] {name} 开始,进程ID: {os.getpid()}")
    result = 0
    for i in range(n):
        result += i ** 2
    print(f"[{time.strftime('%H:%M:%S')}] {name} 完成")
    return result

if __name__ == '__main__':
    print(f"CPU核心数: {cpu_count()}")

    # 并行执行
    processes = []
    for i in range(4):
        p = Process(target=cpu_task, args=(f"任务{i+1}", 10000000))
        processes.append(p)
        p.start()

    for p in processes:
        p.join()

# 输出示例(真正同时执行,如果有4核CPU):
# CPU核心数: 4
# [14:30:00] 任务1 开始,进程ID: 1234
# [14:30:00] 任务2 开始,进程ID: 1235
# [14:30:00] 任务3 开始,进程ID: 1236
# [14:30:00] 任务4 开始,进程ID: 1237
# [14:30:02] 任务1 完成
# [14:30:02] 任务2 完成
# [14:30:02] 任务3 完成
# [14:30:02] 任务4 完成

关键特征:

  • ✅ 任务真正同时执行
  • ✅ 需要多核CPU
  • ✅ 适合CPU密集型任务
  • ✅ 性能提升明显

1.3 进程(Process)

定义: 进程是操作系统进行资源分配和调度的基本单位,拥有独立的内存空间。

特点:

  • 独立的内存空间
  • 进程间通信需要特殊机制(IPC)
  • 创建和销毁开销大
  • 安全隔离性好
  • 不受GIL限制
from multiprocessing import Process, Queue, Pipe
import os
import time

# 场景1:独立计算任务
def calculate_square(numbers, result_queue):
    """计算平方数"""
    pid = os.getpid()
    squares = [n**2 for n in numbers]
    result_queue.put((pid, squares))
    print(f"进程 {pid} 完成计算")

if __name__ == '__main__':
    # 使用队列在进程间传递数据
    result_queue = Queue()

    processes = []
    data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

    for chunk in data:
        p = Process(target=calculate_square, args=(chunk, result_queue))
        processes.append(p)
        p.start()

    for p in processes:
        p.join()

    # 获取所有结果
    results = []
    while not result_queue.empty():
        results.append(result_queue.get())

    print(f"所有结果: {results}")

# 场景2:进程间通信(Pipe)
def sender(conn):
    """发送数据的进程"""
    messages = ['消息1', '消息2', '消息3']
    for msg in messages:
        conn.send(msg)
        print(f"发送: {msg}")
        time.sleep(0.5)
    conn.send('DONE')
    conn.close()

def receiver(conn):
    """接收数据的进程"""
    while True:
        msg = conn.recv()
        if msg == 'DONE':
            break
        print(f"接收: {msg}")
    conn.close()

if __name__ == '__main__':
    # 创建管道
    parent_conn, child_conn = Pipe()

    # 创建进程
    p1 = Process(target=sender, args=(parent_conn,))
    p2 = Process(target=receiver, args=(child_conn,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

使用场景:

  1. CPU密集型计算

    • 科学计算
    • 图像/视频处理
    • 数据分析
    • 机器学习训练
  2. 需要隔离的任务

    • 沙箱执行
    • 第三方代码运行
    • 需要独立崩溃恢复
  3. 多核利用

    • 批量数据处理
    • 并行渲染
    • 分布式计算

1.4 线程(Thread)

定义: 线程是进程内的执行单元,共享进程的内存空间。

特点:

  • 共享内存空间
  • 创建开销小
  • 通信简单直接
  • 受GIL限制(CPython)
  • 需要注意线程安全
import threading
import time
from queue import Queue

# 场景1:生产者-消费者模式
class ProducerConsumer:
    def __init__(self):
        self.queue = Queue(maxsize=10)
        self.lock = threading.Lock()
        self.total_produced = 0
        self.total_consumed = 0

    def producer(self, name, num_items):
        """生产者线程"""
        for i in range(num_items):
            item = f"{name}-item-{i}"
            self.queue.put(item)

            with self.lock:
                self.total_produced += 1

            print(f"[生产者 {name}] 生产: {item}, 队列大小: {self.queue.qsize()}")
            time.sleep(0.1)

    def consumer(self, name):
        """消费者线程"""
        while True:
            try:
                item = self.queue.get(timeout=1)

                with self.lock:
                    self.total_consumed += 1

                print(f"[消费者 {name}] 消费: {item}, 队列大小: {self.queue.qsize()}")
                time.sleep(0.2)

                self.queue.task_done()
            except:
                break

# 运行示例
pc = ProducerConsumer()

# 创建生产者线程
producers = [
    threading.Thread(target=pc.producer, args=(f"P{i}", 5))
    for i in range(2)
]

# 创建消费者线程
consumers = [
    threading.Thread(target=pc.consumer, args=(f"C{i}",), daemon=True)
    for i in range(3)
]

# 启动所有线程
for p in producers:
    p.start()
for c in consumers:
    c.start()

# 等待生产者完成
for p in producers:
    p.join()

# 等待队列清空
pc.queue.join()

print(f"\n生产总数: {pc.total_produced}")
print(f"消费总数: {pc.total_consumed}")

# 场景2:线程池
from concurrent.futures import ThreadPoolExecutor
import requests

def download_url(url):
    """下载网页"""
    print(f"开始下载: {url}")
    response = requests.get(url, timeout=5)
    print(f"完成下载: {url}, 大小: {len(response.content)} bytes")
    return url, len(response.content)

urls = [
    'https://www.python.org',
    'https://www.github.com',
    'https://www.stackoverflow.com',
]

# 使用线程池
with ThreadPoolExecutor(max_workers=3) as executor:
    results = executor.map(download_url, urls)

    for url, size in results:
        print(f"{url}: {size} bytes")

使用场景:

  1. I/O密集型任务

    • 网络请求(爬虫、API调用)
    • 文件读写
    • 数据库查询
  2. GUI应用

    • 保持界面响应
    • 后台任务处理
  3. 并发服务器

    • Web服务器
    • Socket服务器

1.5 协程(Coroutine)

定义: 协程是一种用户态的轻量级线程,由程序自己控制调度,不需要线程上下文切换。

特点:

  • 单线程内执行
  • 极低的切换开销
  • 不需要锁机制
  • 基于事件循环
  • 使用async/await语法
import asyncio
import aiohttp
import time

# 场景1:异步HTTP请求
async def fetch_url(session, url):
    """异步获取URL"""
    print(f"[{time.strftime('%H:%M:%S')}] 开始请求: {url}")
    async with session.get(url) as response:
        data = await response.text()
        print(f"[{time.strftime('%H:%M:%S')}] 完成请求: {url}, 大小: {len(data)}")
        return url, len(data)

async def fetch_all_urls(urls):
    """并发获取多个URL"""
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        return results

# 运行
urls = [
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/2',
    'https://httpbin.org/delay/3',
]

start = time.time()
results = asyncio.run(fetch_all_urls(urls))
elapsed = time.time() - start

print(f"\n总耗时: {elapsed:.2f}秒")
print(f"结果: {results}")

# 场景2:异步文件I/O
import aiofiles

async def read_file_async(filepath):
    """异步读取文件"""
    async with aiofiles.open(filepath, 'r', encoding='utf-8') as f:
        content = await f.read()
        return len(content)

async def process_files(filepaths):
    """并发处理多个文件"""
    tasks = [read_file_async(fp) for fp in filepaths]
    sizes = await asyncio.gather(*tasks)
    return sizes

# 场景3:异步生产者-消费者
async def producer(queue, name, num_items):
    """异步生产者"""
    for i in range(num_items):
        item = f"{name}-{i}"
        await queue.put(item)
        print(f"生产: {item}")
        await asyncio.sleep(0.1)

async def consumer(queue, name):
    """异步消费者"""
    while True:
        item = await queue.get()
        print(f"[消费者 {name}] 消费: {item}")
        await asyncio.sleep(0.2)
        queue.task_done()

async def main():
    queue = asyncio.Queue(maxsize=10)

    # 创建生产者和消费者任务
    producers = [
        asyncio.create_task(producer(queue, f"P{i}", 5))
        for i in range(2)
    ]

    consumers = [
        asyncio.create_task(consumer(queue, f"C{i}"))
        for i in range(3)
    ]

    # 等待生产者完成
    await asyncio.gather(*producers)

    # 等待队列清空
    await queue.join()

    # 取消消费者
    for c in consumers:
        c.cancel()

# asyncio.run(main())

# 场景4:异步数据库操作
import asyncpg

async def fetch_users_async():
    """异步查询数据库"""
    conn = await asyncpg.connect(
        user='user', password='password',
        database='database', host='127.0.0.1'
    )

    # 并发执行多个查询
    queries = [
        conn.fetch('SELECT * FROM users WHERE age > $1', 18),
        conn.fetch('SELECT * FROM orders WHERE status = $1', 'pending'),
        conn.fetch('SELECT * FROM products WHERE price < $1', 100),
    ]

    results = await asyncio.gather(*queries)
    await conn.close()

    return results

使用场景:

  1. 高并发I/O操作

    • 异步Web框架(FastAPI、aiohttp)
    • 爬虫(成千上万个并发请求)
    • WebSocket服务器
  2. 异步数据库操作

    • 大量数据库查询
    • 实时数据处理
  3. 微服务通信

    • 异步RPC调用
    • 消息队列处理

二、并发 vs 并行

对比表格

特性并发并行
执行方式交替执行同时执行
CPU需求单核可以需要多核
资源利用时间片轮转真正的多核利用
适用场景I/O密集型CPU密集型
实现方式线程、协程多进程
性能提升I/O等待时间计算速度

图示对比

并发(Concurrency):
时间 →
CPU: [任务A][任务B][任务A][任务C][任务B][任务A]...
     ↑ 快速切换,看起来同时进行

并行(Parallelism):
时间 →
CPU1: [任务A][任务A][任务A][任务A]...
CPU2: [任务B][任务B][任务B][任务B]...
CPU3: [任务C][任务C][任务C][任务C]...
      ↑ 真正同时执行

实战对比示例

import time
import threading
from multiprocessing import Process
import asyncio

def cpu_bound_task(n):
    """CPU密集型任务:计算"""
    return sum(i * i for i in range(n))

def io_bound_task():
    """I/O密集型任务:模拟网络请求"""
    time.sleep(1)
    return "完成"

# 测试1:CPU密集型 - 串行 vs 并发 vs 并行
def test_cpu_intensive():
    n = 10000000

    # 串行
    start = time.time()
    for _ in range(4):
        cpu_bound_task(n)
    serial_time = time.time() - start

    # 并发(线程)- GIL限制,性能差
    start = time.time()
    threads = [threading.Thread(target=cpu_bound_task, args=(n,)) for _ in range(4)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    concurrent_time = time.time() - start

    # 并行(进程)- 真正的并行
    start = time.time()
    processes = [Process(target=cpu_bound_task, args=(n,)) for _ in range(4)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    parallel_time = time.time() - start

    print("CPU密集型任务对比:")
    print(f"串行: {serial_time:.2f}秒")
    print(f"并发(线程): {concurrent_time:.2f}秒")
    print(f"并行(进程): {parallel_time:.2f}秒")
    print(f"并行提速: {serial_time/parallel_time:.2f}x\n")

# 测试2:I/O密集型 - 串行 vs 并发
def test_io_intensive():
    # 串行
    start = time.time()
    for _ in range(10):
        io_bound_task()
    serial_time = time.time() - start

    # 并发(线程)
    start = time.time()
    threads = [threading.Thread(target=io_bound_task) for _ in range(10)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    concurrent_time = time.time() - start

    # 协程
    async def async_io_task():
        await asyncio.sleep(1)
        return "完成"

    async def test_coroutine():
        tasks = [async_io_task() for _ in range(10)]
        await asyncio.gather(*tasks)

    start = time.time()
    asyncio.run(test_coroutine())
    coroutine_time = time.time() - start

    print("I/O密集型任务对比:")
    print(f"串行: {serial_time:.2f}秒")
    print(f"并发(线程): {concurrent_time:.2f}秒")
    print(f"协程: {coroutine_time:.2f}秒")
    print(f"并发提速: {serial_time/concurrent_time:.2f}x")
    print(f"协程提速: {serial_time/coroutine_time:.2f}x")

# 运行测试
if __name__ == '__main__':
    test_cpu_intensive()
    test_io_intensive()

三、进程 vs 线程 vs 协程

详细对比表

特性进程线程协程
调度者操作系统操作系统程序自己
切换开销大(ms级)中(μs级)小(ns级)
内存独立共享共享
通信IPC(复杂)共享内存(简单)函数调用(最简单)
数据安全完全隔离需要锁不需要锁
创建数量几十个几百个几万个
GIL影响无(单线程)
CPU利用多核并行受GIL限制单核并发
适用场景CPU密集型I/O密集型高并发I/O

内存模型对比

import os
import threading
import asyncio
from multiprocessing import Process, Value

# 全局变量
global_counter = 0

# 进程示例:独立内存空间
def process_increment(shared_value):
    """进程:修改共享内存需要特殊机制"""
    for _ in range(100000):
        shared_value.value += 1
    print(f"进程 {os.getpid()} 完成,值: {shared_value.value}")

def test_process_memory():
    # 使用Value创建共享内存
    shared_counter = Value('i', 0)

    processes = [
        Process(target=process_increment, args=(shared_counter,))
        for _ in range(4)
    ]

    for p in processes:
        p.start()
    for p in processes:
        p.join()

    print(f"进程最终值: {shared_counter.value}")

# 线程示例:共享内存空间
def thread_increment():
    """线程:直接访问全局变量,需要锁保护"""
    global global_counter
    for _ in range(100000):
        global_counter += 1  # 不安全!

def thread_increment_safe(lock):
    """线程:使用锁保护"""
    global global_counter
    for _ in range(100000):
        with lock:
            global_counter += 1

def test_thread_memory():
    global global_counter

    # 不安全的版本
    global_counter = 0
    threads = [threading.Thread(target=thread_increment) for _ in range(4)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    print(f"线程不安全版本: {global_counter} (应该是400000)")

    # 安全的版本
    global_counter = 0
    lock = threading.Lock()
    threads = [threading.Thread(target=thread_increment_safe, args=(lock,)) for _ in range(4)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    print(f"线程安全版本: {global_counter}")

# 协程示例:单线程,不需要锁
async def coroutine_increment(counter):
    """协程:单线程执行,天然线程安全"""
    for _ in range(100000):
        counter['value'] += 1

async def test_coroutine_memory():
    counter = {'value': 0}

    tasks = [coroutine_increment(counter) for _ in range(4)]
    await asyncio.gather(*tasks)

    print(f"协程版本: {counter['value']}")

if __name__ == '__main__':
    print("=== 内存模型测试 ===\n")
    test_process_memory()
    test_thread_memory()
    asyncio.run(test_coroutine_memory())

四、CPU密集型 vs IO密集型

CPU密集型任务(CPU-bound)

定义: 主要消耗CPU资源的任务,大部分时间在进行计算。

特征:

  • CPU使用率高(接近100%)
  • 内存访问频繁
  • 很少等待I/O
  • 受CPU性能限制

典型场景:

import time
import math
from multiprocessing import Process, Pool
import numpy as np

# 场景1:数学计算
def calculate_pi(n):
    """蒙特卡洛法计算π"""
    inside = 0
    for _ in range(n):
        x = np.random.random()
        y = np.random.random()
        if x*x + y*y <= 1:
            inside += 1
    return 4 * inside / n

# 场景2:图像处理
def process_image(image_data):
    """图像处理:滤波、变换等"""
    # 高斯模糊
    result = np.zeros_like(image_data)
    for i in range(1, image_data.shape[0]-1):
        for j in range(1, image_data.shape[1]-1):
            # 3x3卷积核
            result[i,j] = np.sum(image_data[i-1:i+2, j-1:j+2]) / 9
    return result

# 场景3:加密解密
def encrypt_data(data, key):
    """模拟加密操作"""
    result = []
    for char in data:
        encrypted = (ord(char) + key) % 256
        result.append(chr(encrypted))
    return ''.join(result)

# 场景4:数据压缩
def compress_data(data):
    """简单的RLE压缩"""
    if not data:
        return []

    compressed = []
    count = 1
    prev = data[0]

    for char in data[1:]:
        if char == prev:
            count += 1
        else:
            compressed.append((prev, count))
            prev = char
            count = 1
    compressed.append((prev, count))

    return compressed

# 场景5:机器学习训练
def train_model(X, y, epochs):
    """简化的神经网络训练"""
    weights = np.random.randn(X.shape[1])
    lr = 0.01

    for epoch in range(epochs):
        # 前向传播
        predictions = np.dot(X, weights)

        # 计算损失
        loss = np.mean((predictions - y) ** 2)

        # 反向传播
        gradients = 2 * np.dot(X.T, (predictions - y)) / len(y)

        # 更新权重
        weights -= lr * gradients

    return weights

# CPU密集型任务的最佳实践:使用多进程
def optimal_cpu_bound():
    """CPU密集型任务的最佳并行方案"""

    # 方案1:使用进程池
    with Pool(processes=4) as pool:
        # 分配任务到多个进程
        results = pool.map(calculate_pi, [10000000] * 4)
        avg_pi = sum(results) / len(results)
        print(f"计算的π值: {avg_pi}")

    # 方案2:使用numpy的向量化
    # numpy内部使用C实现,自动并行
    large_array = np.random.random((1000, 1000))
    result = np.dot(large_array, large_array.T)  # 自动优化

性能对比:

import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

def cpu_intensive_task(n):
    """CPU密集型:计算素数"""
    def is_prime(num):
        if num < 2:
            return False
        for i in range(2, int(num**0.5) + 1):
            if num % i == 0:
                return False
        return True

    return sum(1 for i in range(n) if is_prime(i))

def compare_cpu_bound():
    n = 100000
    tasks = [n] * 4

    # 串行
    start = time.time()
    results = [cpu_intensive_task(t) for t in tasks]
    serial_time = time.time() - start

    # 线程池(GIL限制,性能差)
    start = time.time()
    with ThreadPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(cpu_intensive_task, tasks))
    thread_time = time.time() - start

    # 进程池(真正并行,性能好)
    start = time.time()
    with ProcessPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(cpu_intensive_task, tasks))
    process_time = time.time() - start

    print("CPU密集型任务性能对比:")
    print(f"串行: {serial_time:.2f}秒")
    print(f"线程池: {thread_time:.2f}秒 (提速 {serial_time/thread_time:.2f}x)")
    print(f"进程池: {process_time:.2f}秒 (提速 {serial_time/process_time:.2f}x)")

if __name__ == '__main__':
    compare_cpu_bound()

IO密集型任务(IO-bound)

定义: 主要时间花在等待I/O操作完成的任务,CPU大部分时间空闲。

特征:

  • CPU使用率低
  • 大量时间等待I/O
  • 网络、磁盘等外部资源是瓶颈
  • 并发可以显著提升性能

典型场景:

import time
import requests
import aiohttp
import asyncio
from concurrent.futures import ThreadPoolExecutor
import aiofiles

# 场景1:网络请求
def fetch_url_sync(url):
    """同步网络请求"""
    response = requests.get(url, timeout=5)
    return len(response.content)

async def fetch_url_async(session, url):
    """异步网络请求"""
    async with session.get(url) as response:
        content = await response.read()
        return len(content)

# 场景2:文件I/O
def read_file_sync(filepath):
    """同步文件读取"""
    with open(filepath, 'r', encoding='utf-8') as f:
        return f.read()

async def read_file_async(filepath):
    """异步文件读取"""
    async with aiofiles.open(filepath, 'r', encoding='utf-8') as f:
        return await f.read()

def write_file_sync(filepath, content):
    """同步文件写入"""
    with open(filepath, 'w', encoding='utf-8') as f:
        f.write(content)

async def write_file_async(filepath, content):
    """异步文件写入"""
    async with aiofiles.open(filepath, 'w', encoding='utf-8') as f:
        await f.write(content)

# 场景3:数据库操作
import sqlite3
import asyncpg

def query_database_sync(query):
    """同步数据库查询"""
    conn = sqlite3.connect('example.db')
    cursor = conn.cursor()
    cursor.execute(query)
    results = cursor.fetchall()
    conn.close()
    return results

async def query_database_async(query):
    """异步数据库查询"""
    conn = await asyncpg.connect('postgresql://localhost/db')
    results = await conn.fetch(query)
    await conn.close()
    return results

# 场景4:API调用
class APIClient:
    """API客户端示例"""

    def get_user_sync(self, user_id):
        """同步获取用户信息"""
        response = requests.get(f'https://api.example.com/users/{user_id}')
        return response.json()

    async def get_user_async(self, session, user_id):
        """异步获取用户信息"""
        async with session.get(f'https://api.example.com/users/{user_id}') as response:
            return await response.json()

    async def get_multiple_users(self, user_ids):
        """并发获取多个用户"""
        async with aiohttp.ClientSession() as session:
            tasks = [self.get_user_async(session, uid) for uid in user_ids]
            return await asyncio.gather(*tasks)

# I/O密集型任务的最佳实践
async def optimal_io_bound():
    """I/O密集型任务的最佳并发方案"""

    urls = [
        'https://httpbin.org/delay/1',
        'https://httpbin.org/delay/2',
        'https://httpbin.org/delay/3',
    ] * 10  # 30个请求

    # 方案1:使用asyncio + aiohttp
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url_async(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        print(f"异步完成 {len(results)} 个请求")

    # 方案2:使用线程池(次优)
    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(fetch_url_sync, urls))
        print(f"线程池完成 {len(results)} 个请求")

# 性能对比
def compare_io_bound():
    """I/O密集型任务性能对比"""

    def io_task():
        """模拟I/O操作"""
        time.sleep(0.5)
        return "完成"

    async def async_io_task():
        """模拟异步I/O"""
        await asyncio.sleep(0.5)
        return "完成"

    tasks_count = 20

    # 串行
    start = time.time()
    results = [io_task() for _ in range(tasks_count)]
    serial_time = time.time() - start

    # 线程池
    start = time.time()
    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(lambda x: io_task(), range(tasks_count)))
    thread_time = time.time() - start

    # 协程
    async def run_async():
        tasks = [async_io_task() for _ in range(tasks_count)]
        return await asyncio.gather(*tasks)

    start = time.time()
    results = asyncio.run(run_async())
    async_time = time.time() - start

    print("I/O密集型任务性能对比:")
    print(f"串行: {serial_time:.2f}秒")
    print(f"线程池: {thread_time:.2f}秒 (提速 {serial_time/thread_time:.2f}x)")
    print(f"协程: {async_time:.2f}秒 (提速 {serial_time/async_time:.2f}x)")

if __name__ == '__main__':
    compare_io_bound()

五、内部实现原理深度剖析

5.1 多进程内部实现原理

5.1.1 进程的创建过程(fork/spawn)

Unix/Linux系统(fork模式)

import os
import sys

def demonstrate_fork():
    """演示fork的工作原理"""
    print(f"父进程开始,PID: {os.getpid()}")

    # fork创建子进程
    # 在父进程中返回子进程PID,在子进程中返回0
    pid = os.fork()

    if pid > 0:
        # 父进程代码
        print(f"我是父进程,PID: {os.getpid()}, 子进程PID: {pid}")
        os.wait()  # 等待子进程结束
    else:
        # 子进程代码
        print(f"我是子进程,PID: {os.getpid()}, 父进程PID: {os.getppid()}")
        sys.exit(0)

# 注意:fork在Windows上不可用

# fork的内部机制:
# 1. 操作系统调用fork()系统调用
# 2. 复制父进程的整个地址空间(采用写时复制COW优化)
# 3. 子进程获得父进程的代码、数据、堆、栈的副本
# 4. 两个进程拥有独立的内存空间
# 5. 文件描述符被复制(但指向同一个文件表)

写时复制(Copy-On-Write, COW)机制

初始状态(fork后):
父进程内存页 ──→ [只读共享物理内存]
                    ↑
子进程内存页 ──────┘

写操作触发:
父进程内存页 ──→ [原物理内存页]
子进程内存页 ──→ [新复制的物理内存页]
                 ↑ 只有在写入时才真正复制

优点:
✅ 节省内存
✅ 加快fork速度
✅ 大部分只读数据永远不需要复制

Windows系统(spawn模式)

from multiprocessing import Process, set_start_method
import os

def worker(name):
    """工作进程"""
    print(f"子进程 {name}, PID: {os.getpid()}")

if __name__ == '__main__':
    # Windows默认使用spawn模式
    # spawn不是fork,而是启动全新的Python解释器

    # 可以显式设置启动方法
    # set_start_method('spawn')  # Windows默认
    # set_start_method('fork')   # Unix/Linux默认
    # set_start_method('forkserver')  # 混合模式

    p = Process(target=worker, args=('Worker-1',))
    p.start()
    p.join()

# spawn的内部机制:
# 1. 创建新的Python解释器进程
# 2. 导入必要的模块
# 3. 通过pickle序列化传递函数和参数
# 4. 在新进程中反序列化并执行
# 5. 完全独立的进程,没有共享内存

5.1.2 进程间通信(IPC)机制

1. 管道(Pipe)

from multiprocessing import Process, Pipe
import os

def pipe_internals():
    """管道内部原理演示"""

    # 创建管道
    parent_conn, child_conn = Pipe()

    # 管道底层实现:
    # Unix: 使用pipe()系统调用创建内核缓冲区
    # Windows: 使用命名管道或匿名管道

    def sender(conn):
        print(f"发送进程 PID: {os.getpid()}")
        # 数据写入管道
        # 1. 序列化对象(pickle)
        # 2. 写入内核缓冲区
        # 3. 接收进程从缓冲区读取
        conn.send({'data': 'Hello'})
        conn.close()

    def receiver(conn):
        print(f"接收进程 PID: {os.getpid()}")
        # 从管道读取
        # 1. 从内核缓冲区读取字节
        # 2. 反序列化为Python对象
        data = conn.recv()
        print(f"接收到: {data}")
        conn.close()

    p1 = Process(target=sender, args=(parent_conn,))
    p2 = Process(target=receiver, args=(child_conn,))

    p1.start()
    p2.start()
    p1.join()
    p2.join()

# 管道的底层结构:
"""
进程A                     内核空间                    进程B
写端 ──→ [发送缓冲区] ──→ [管道缓冲区] ──→ [接收缓冲区] ──→ 读端
          ↓                  ↓                  ↓
       pickle序列化      内核管理的FIFO      pickle反序列化

特点:
- 半双工或全双工(取决于实现)
- 有限的缓冲区大小(通常64KB)
- 阻塞式读写
- 数据流式传输
"""

2. 队列(Queue)

from multiprocessing import Process, Queue
import queue

def queue_internals():
    """队列内部原理"""

    q = Queue(maxsize=10)

    # Queue内部结构:
    # 1. 底层使用Pipe实现
    # 2. 额外的线程用于读写
    # 3. 使用锁保证线程安全
    # 4. 使用信号量控制大小

    def producer(queue):
        for i in range(5):
            # put内部流程:
            # 1. 获取信号量(检查是否已满)
            # 2. 获取锁
            # 3. pickle序列化数据
            # 4. 写入管道
            # 5. 释放锁
            # 6. 通知非空条件变量
            queue.put(i)
            print(f"生产: {i}")

    def consumer(queue):
        while True:
            try:
                # get内部流程:
                # 1. 获取非空条件变量
                # 2. 获取锁
                # 3. 从管道读取
                # 4. pickle反序列化
                # 5. 释放锁
                # 6. 释放信号量
                item = queue.get(timeout=1)
                print(f"消费: {item}")
            except queue.Empty:
                break

    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))

    p1.start()
    p2.start()
    p1.join()
    p2.join()

# Queue的内部架构:
"""
┌─────────────────────────────────────┐
│         multiprocessing.Queue        │
├─────────────────────────────────────┤
│  _writer (Thread) │ _reader (Thread)│
│         ↓         │        ↑         │
│    ┌──────────────┴────────────┐    │
│    │      Pipe (管道)          │    │
│    └───────────────────────────┘    │
│                                      │
│  Semaphore (信号量) - 控制容量       │
│  Lock (锁) - 保护内部状态            │
│  Condition (条件变量) - 等待/通知    │
└─────────────────────────────────────┘

工作流程:
1. put() → 序列化 → Writer线程 → Pipe → Reader线程 → 反序列化 → get()
2. 使用线程避免阻塞主进程
3. 缓冲区满时put()阻塞,空时get()阻塞
"""

3. 共享内存(Shared Memory)

from multiprocessing import Process, Value, Array, shared_memory
import numpy as np

def shared_memory_internals():
    """共享内存内部原理"""

    # 方式1:Value和Array(简单类型)
    # 底层使用mmap(内存映射文件)
    shared_int = Value('i', 0)  # 共享整数
    shared_array = Array('i', [1, 2, 3, 4, 5])  # 共享数组

    # 内部实现:
    # 1. 在操作系统中分配共享内存段
    # 2. 使用mmap映射到每个进程的地址空间
    # 3. 所有进程看到同一块物理内存
    # 4. 需要锁保护并发访问

    def worker(val, arr):
        # 访问共享内存需要获取锁
        with val.get_lock():
            val.value += 1

        with arr.get_lock():
            arr[0] += 1

    processes = [Process(target=worker, args=(shared_int, shared_array)) for _ in range(10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()

    print(f"共享整数: {shared_int.value}")  # 10
    print(f"共享数组: {list(shared_array)}")  # [11, 2, 3, 4, 5]

    # 方式2:SharedMemory(Python 3.8+,更高级)
    shm = shared_memory.SharedMemory(create=True, size=1024)

    # 共享NumPy数组
    arr = np.ndarray((10,), dtype=np.int64, buffer=shm.buf)
    arr[:] = range(10)

    def use_shared_numpy(shm_name):
        # 子进程附加到共享内存
        existing_shm = shared_memory.SharedMemory(name=shm_name)
        arr = np.ndarray((10,), dtype=np.int64, buffer=existing_shm.buf)
        arr[:] = arr * 2  # 直接修改共享内存
        existing_shm.close()

    p = Process(target=use_shared_numpy, args=(shm.name,))
    p.start()
    p.join()

    print(f"共享NumPy数组: {arr}")  # [0, 2, 4, 6, ...]

    shm.close()
    shm.unlink()

# 共享内存的底层原理:
"""
进程A地址空间          物理内存          进程B地址空间
┌────────────┐      ┌──────────┐      ┌────────────┐
│ 虚拟地址A  │─────→│ 共享内存段 │←─────│ 虚拟地址B  │
└────────────┘      └──────────┘      └────────────┘
                         ↑
                    同一块物理内存

实现方式(Unix/Linux):
1. System V 共享内存: shmget(), shmat()
2. POSIX 共享内存: shm_open(), mmap()
3. 内存映射文件: mmap()

实现方式(Windows):
1. CreateFileMapping()
2. MapViewOfFile()

关键特性:
✅ 最快的IPC方式(无需拷贝)
✅ 直接访问物理内存
❌ 需要同步机制(锁)
❌ 没有自动序列化
"""

5.1.3 进程池(ProcessPool)实现

from multiprocessing import Pool, cpu_count
import os
import time

class ProcessPoolInternals:
    """进程池内部机制演示"""

    @staticmethod
    def worker_init():
        """工作进程初始化"""
        print(f"工作进程初始化: PID={os.getpid()}")

    @staticmethod
    def demonstrate_pool():
        """进程池工作原理"""

        # 创建进程池
        with Pool(processes=4, initializer=ProcessPoolInternals.worker_init) as pool:
            # 进程池内部结构:
            # 1. 创建固定数量的工作进程(预先fork)
            # 2. 维护任务队列(输入队列)
            # 3. 维护结果队列(输出队列)
            # 4. 主进程分发任务到工作进程
            # 5. 工作进程从任务队列获取任务
            # 6. 执行完成后将结果放入结果队列

            def task(x):
                time.sleep(0.1)
                return x * x

            # map内部流程:
            # 1. 将任务分成多个chunk
            # 2. 每个chunk作为一个任务放入任务队列
            # 3. 工作进程并行处理
            # 4. 收集所有结果并按顺序返回
            results = pool.map(task, range(10))
            print(f"结果: {results}")

# 进程池的内部架构:
"""
                主进程
                  │
    ┌─────────────┼─────────────┐
    │             │             │
    ↓             ↓             ↓
┌────────┐  ┌────────┐  ┌────────┐
│工作进程1│  │工作进程2│  │工作进程3│
└────────┘  └────────┘  └────────┘
    ↑             ↑             ↑
    └─────────────┴─────────────┘
              任务队列
    ┌─────────────────────────┐
    │ [任务1] [任务2] [任务3] │
    └─────────────────────────┘
              结果队列
    ┌─────────────────────────┐
    │ [结果1] [结果2] [结果3] │
    └─────────────────────────┘

工作流程:
1. 初始化时预创建N个工作进程
2. 主进程将任务序列化后放入任务队列
3. 空闲的工作进程从队列获取任务
4. 执行任务并将结果放入结果队列
5. 主进程收集结果
6. 进程复用,避免频繁创建销毁

优化策略:
- Chunk分块:减少任务分发开销
- 进程复用:避免创建销毁开销
- 缓冲队列:平衡生产消费速度
"""

5.2 多线程内部实现原理

2.1 GIL(全局解释器锁)深入解析

GIL的本质

import threading
import sys
import dis

def demonstrate_gil():
    """演示GIL的影响"""

    # GIL是CPython解释器级别的互斥锁
    # 作用:保护Python对象的引用计数不被竞态条件破坏

    counter = 0

    def increment():
        global counter
        # 这个简单操作在字节码层面不是原子的
        for _ in range(1000000):
            counter += 1

    # 查看字节码
    print("字节码分析:")
    dis.dis(increment)

    # counter += 1 对应的字节码(简化):
    # LOAD_GLOBAL  counter    # 1. 读取counter的值
    # LOAD_CONST   1          # 2. 加载常量1
    # BINARY_ADD              # 3. 执行加法
    # STORE_GLOBAL counter    # 4. 存储结果

    # 这4条指令之间可能发生线程切换!

    t1 = threading.Thread(target=increment)
    t2 = threading.Thread(target=increment)

    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print(f"最终值: {counter}")  # 可能不是2000000!

# GIL的工作机制:
"""
时间线:
Thread 1: [获取GIL]──[执行Python代码]──[释放GIL]──[等待]──[获取GIL]──...
Thread 2: [等待GIL]──────────────────[获取GIL]──[执行]──[释放]──...

GIL释放时机:
1. 执行固定数量的字节码指令(默认100条,Python 3中改为检查机制)
2. I/O操作时(如read(), write(), sleep())
3. 调用C扩展(如果扩展释放GIL)
4. 长时间运行的操作(如time.sleep())

检查机制(Python 3.2+):
- 不再按指令数,改为按时间间隔(默认5ms)
- 使用gil_drop_request标志位
- 等待线程可以请求当前线程释放GIL
- 减少了不必要的上下文切换

伪代码:
while True:
    获取GIL
    执行Python字节码
    if 时间超过5ms or gil_drop_request:
        释放GIL
        等待其他线程
        重新竞争GIL
"""

GIL的底层实现(C代码级别)

// Python/ceval_gil.h (简化版本)

struct _gil_runtime_state {
    unsigned long interval;        // 检查间隔(纳秒)
    _Py_atomic_int locked;        // GIL是否被持有
    unsigned long switch_number;   // 切换计数

    // 条件变量和互斥锁
    pthread_cond_t cond;          // 等待条件
    pthread_mutex_t mutex;        // 保护GIL状态
    pthread_cond_t switch_cond;   // 切换条件
    pthread_mutex_t switch_mutex; // 切换互斥锁
};

// 获取GIL
static void take_gil(PyThreadState *tstate) {
    pthread_mutex_lock(&gil.mutex);

    while (gil.locked) {
        // GIL被其他线程持有,等待
        pthread_cond_wait(&gil.cond, &gil.mutex);
    }

    // 获取GIL
    gil.locked = 1;
    pthread_mutex_unlock(&gil.mutex);
}

// 释放GIL
static void drop_gil(PyThreadState *tstate) {
    pthread_mutex_lock(&gil.mutex);
    gil.locked = 0;

    // 唤醒等待的线程
    pthread_cond_signal(&gil.cond);
    pthread_mutex_unlock(&gil.mutex);
}

绕过GIL的方法

import ctypes
import numpy as np
from numba import jit, prange

def bypass_gil_methods():
    """绕过GIL的各种方法"""

    # 方法1:使用释放GIL的C扩展
    # NumPy在底层计算时释放GIL
    def numpy_computation():
        arr1 = np.random.random((1000, 1000))
        arr2 = np.random.random((1000, 1000))
        # 这个操作会释放GIL
        result = np.dot(arr1, arr2)
        return result

    # 方法2:使用Numba编译(带nogil选项)
    @jit(nopython=True, nogil=True)
    def numba_computation(n):
        # 这个函数编译为机器码,不需要GIL
        result = 0
        for i in prange(n):
            result += i * i
        return result

    # 方法3:使用ctypes调用C函数
    libc = ctypes.CDLL(None)

    # C的sleep不需要GIL
    def c_sleep(seconds):
        libc.sleep(seconds)

    # 方法4:使用threading配合I/O操作
    # I/O操作会自动释放GIL
    import time
    def io_operation():
        time.sleep(1)  # 释放GIL
        with open('file.txt', 'r') as f:
            data = f.read()  # 释放GIL

# GIL释放示例图:
"""
线程1执行NumPy操作:
[持有GIL]──[调用NumPy]──[释放GIL]──[C代码执行]──[重新获取GIL]──[返回]
                            ↓
线程2可以并行执行:          [获取GIL]──[执行Python代码]──[释放GIL]

结果:虽然是线程,但NumPy操作可以真正并行!
"""

2.2 线程调度与上下文切换

import threading
import time
import os

def thread_scheduling_demo():
    """线程调度演示"""

    class ThreadSchedulingMonitor:
        def __init__(self):
            self.switch_count = 0
            self.lock = threading.Lock()

        def cpu_bound_task(self, thread_id):
            """CPU密集型任务"""
            last_time = time.time()

            for i in range(10):
                # 模拟计算
                result = sum(j * j for j in range(100000))

                current_time = time.time()
                if current_time - last_time > 0.005:  # 检测到可能的上下文切换
                    with self.lock:
                        self.switch_count += 1
                    print(f"线程{thread_id}: 可能发生了上下文切换")

                last_time = current_time

    monitor = ThreadSchedulingMonitor()

    threads = [
        threading.Thread(target=monitor.cpu_bound_task, args=(i,))
        for i in range(4)
    ]

    for t in threads:
        t.start()
    for t in threads:
        t.join()

    print(f"总切换次数: {monitor.switch_count}")

# 上下文切换的内容:
"""
保存当前线程状态:
1. CPU寄存器值(PC、SP、通用寄存器)
2. 线程栈信息
3. 线程局部存储(TLS)
4. GIL状态

恢复新线程状态:
1. 加载保存的寄存器值
2. 切换栈指针
3. 恢复TLS
4. 获取GIL(如果是Python线程)

开销分析:
- 直接开销:保存/恢复寄存器(纳秒级)
- 间接开销:
  * CPU缓存失效(微秒级)
  * TLB刷新(微秒级)
  * 流水线停顿
  * GIL竞争(Python特有,微秒到毫秒级)

┌─────────────────────────────────────────┐
│     线程上下文切换时间线                 │
├─────────────────────────────────────────┤
│ T1执行 → 保存T1 → 调度决策 → 恢复T2 → T2执行 │
│  [5μs]   [1μs]    [2μs]     [1μs]   [5μs]  │
│                                          │
│ 总开销:约4μs + 缓存失效开销             │
└─────────────────────────────────────────┘
"""

2.3 线程同步原语的底层实现

import threading
import time

class LockInternals:
    """锁的内部实现原理"""

    @staticmethod
    def demonstrate_lock_internals():
        """演示锁的工作原理"""

        # threading.Lock底层使用pthread_mutex_t(Unix)或CRITICAL_SECTION(Windows)
        lock = threading.Lock()

        # Lock的内部状态:
        # - locked: bool(是否已被获取)
        # - owner: thread_id(持有锁的线程ID)
        # - waiters: queue(等待队列)

        def worker(worker_id):
            print(f"线程{worker_id}尝试获取锁...")

            # lock.acquire()的内部流程:
            # 1. 原子操作检查locked标志
            # 2. 如果未锁定,设置locked=True,设置owner=当前线程
            # 3. 如果已锁定,将当前线程加入等待队列
            # 4. 调用pthread_cond_wait()等待唤醒
            # 5. 被唤醒后重新竞争锁

            lock.acquire()
            print(f"线程{worker_id}获得锁")
            time.sleep(0.1)

            # lock.release()的内部流程:
            # 1. 检查owner是否是当前线程
            # 2. 设置locked=False
            # 3. 从等待队列唤醒一个线程(pthread_cond_signal)
            # 4. 被唤醒的线程竞争锁

            lock.release()
            print(f"线程{worker_id}释放锁")

        threads = [threading.Thread(target=worker, args=(i,)) for i in range(3)]
        for t in threads:
            t.start()
        for t in threads:
            t.join()

# Lock的底层实现(伪代码):
"""
class Lock:
    def __init__(self):
        self.locked = False
        self.owner = None
        self.waiters = Queue()
        self.mutex = pthread_mutex_t()  # 保护内部状态
        self.cond = pthread_cond_t()    # 条件变量

    def acquire(self):
        pthread_mutex_lock(&self.mutex)

        while self.locked:
            # 加入等待队列
            self.waiters.push(current_thread)
            # 等待条件变量(自动释放mutex)
            pthread_cond_wait(&self.cond, &self.mutex)

        # 获取锁
        self.locked = True
        self.owner = current_thread

        pthread_mutex_unlock(&self.mutex)

    def release(self):
        pthread_mutex_lock(&self.mutex)

        if self.owner != current_thread:
            raise RuntimeError("释放未持有的锁")

        self.locked = False
        self.owner = None

        # 唤醒一个等待线程
        if not self.waiters.empty():
            pthread_cond_signal(&self.cond)

        pthread_mutex_unlock(&self.mutex)
"""

class ConditionInternals:
    """条件变量的内部实现"""

    @staticmethod
    def demonstrate_condition():
        """演示条件变量"""

        condition = threading.Condition()
        items = []

        def producer():
            for i in range(5):
                time.sleep(0.5)

                # 条件变量的典型用法
                with condition:
                    items.append(i)
                    print(f"生产: {i}")
                    # notify()内部:
                    # 1. 从等待队列中选择一个线程
                    # 2. 将其移到就绪队列
                    # 3. 发送信号唤醒
                    condition.notify()

        def consumer():
            while True:
                with condition:
                    # wait()内部:
                    # 1. 释放锁
                    # 2. 将当前线程加入等待队列
                    # 3. 等待被notify唤醒
                    # 4. 被唤醒后重新获取锁
                    while not items:
                        condition.wait()

                    item = items.pop(0)
                    print(f"消费: {item}")

                    if item == 4:
                        break

        t1 = threading.Thread(target=producer)
        t2 = threading.Thread(target=consumer)

        t2.start()
        time.sleep(0.1)
        t1.start()

        t1.join()
        t2.join()

# 条件变量的工作流程:
"""
消费者线程:
1. 获取锁
2. 检查条件(队列是否为空)
3. 如果条件不满足,调用wait():
   a. 释放锁
   b. 进入等待状态
   c. 被唤醒后重新获取锁
   d. 重新检查条件
4. 条件满足,执行操作
5. 释放锁

生产者线程:
1. 获取锁
2. 修改共享状态(放入数据)
3. 调用notify()唤醒等待线程
4. 释放锁

时间线:
消费者: [获取锁]─[检查空]─[wait释放锁]─────[被唤醒]─[重新获取锁]─[消费]
生产者: ─────────────────[获取锁]─[生产]─[notify]─[释放锁]
"""

5.3 协程内部实现原理

5.3.1 事件循环(Event Loop)核心机制

import asyncio
import selectors
import socket

class EventLoopInternals:
    """事件循环内部原理演示"""

    @staticmethod
    def simple_event_loop():
        """简化的事件循环实现"""

        # 事件循环的核心组件
        class SimpleEventLoop:
            def __init__(self):
                # 选择器:监听I/O事件
                self.selector = selectors.DefaultSelector()
                # 就绪队列:可以立即执行的协程
                self.ready = []
                # 调度队列:延迟执行的协程
                self.scheduled = []
                # 当前时间
                self.time = 0

            def create_task(self, coro):
                """创建任务"""
                task = Task(coro, self)
                self.ready.append(task)
                return task

            def call_soon(self, callback, *args):
                """尽快调用回调"""
                self.ready.append((callback, args))

            def call_later(self, delay, callback, *args):
                """延迟调用"""
                when = self.time + delay
                self.scheduled.append((when, callback, args))
                self.scheduled.sort()  # 按时间排序

            def run_forever(self):
                """运行事件循环"""
                while True:
                    # 1. 处理就绪队列
                    while self.ready:
                        callback = self.ready.pop(0)
                        if isinstance(callback, Task):
                            callback.step()
                        else:
                            func, args = callback
                            func(*args)

                    # 2. 处理调度队列
                    self.time = time.time()
                    while self.scheduled and self.scheduled[0][0] <= self.time:
                        when, callback, args = self.scheduled.pop(0)
                        callback(*args)

                    # 3. 等待I/O事件
                    timeout = None
                    if self.scheduled:
                        timeout = max(0, self.scheduled[0][0] - self.time)

                    # select()系统调用:等待I/O就绪
                    events = self.selector.select(timeout)

                    for key, mask in events:
                        callback = key.data
                        callback(key.fileobj, mask)

                    # 如果没有任务,退出
                    if not self.ready and not self.scheduled:
                        break

        # 简单的Task实现
        class Task:
            def __init__(self, coro, loop):
                self.coro = coro
                self.loop = loop

            def step(self):
                try:
                    # 恢复协程执行
                    result = self.coro.send(None)
                    # 如果返回值,说明还没完成
                    if result is not None:
                        self.loop.ready.append(self)
                except StopIteration:
                    # 协程完成
                    pass

        # 使用示例
        async def my_coroutine():
            print("协程开始")
            await asyncio.sleep(1)
            print("协程结束")

        loop = SimpleEventLoop()
        loop.create_task(my_coroutine())
        loop.run_forever()

# 事件循环的完整架构:
"""
┌─────────────────────────────────────────────────────────┐
│                    Event Loop (事件循环)                  │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  ┌────────────┐  ┌────────────┐  ┌─────────────┐       │
│  │ Ready Queue│  │Scheduled Q │  │  Selector   │       │
│  │ (就绪队列) │  │ (调度队列) │  │  (I/O多路复用) │     │
│  └────────────┘  └────────────┘  └─────────────┘       │
│        │               │                  │             │
│        ↓               ↓                  ↓             │
│  ┌──────────────────────────────────────────────┐      │
│  │         Main Loop (主循环)                    │      │
│  ├──────────────────────────────────────────────┤      │
│  │ 1. 执行就绪任务                               │      │
│  │ 2. 执行到期的调度任务                         │      │
│  │ 3. 等待I/O事件 (select/epoll/kqueue)        │      │
│  │ 4. 处理I/O回调                               │      │
│  │ 5. 重复                                      │      │
│  └──────────────────────────────────────────────┘      │
│                                                          │
└─────────────────────────────────────────────────────────┘

关键系统调用:
- Unix: select(), poll(), epoll_wait()
- Windows: select(), IOCP
- macOS: kqueue()

工作流程:
1. 应用创建协程
2. 协程注册到事件循环
3. 事件循环调度协程执行
4. 协程遇到await,挂起并返回控制权
5. 事件循环继续执行其他协程
6. I/O完成时,通过回调恢复协程
"""

3.2 协程的状态机与调度

import asyncio
import inspect

class CoroutineInternals:
    """协程内部状态演示"""

    @staticmethod
    async def demonstrate_coroutine_states():
        """演示协程的状态"""

        # 协程的生命周期状态:
        # CORO_CREATED  → CORO_RUNNING → CORO_SUSPENDED → CORO_CLOSED
        #    (创建)         (运行中)        (挂起)          (关闭)

        async def example_coroutine():
            print("协程开始")
            # 执行await时,协程从RUNNING变为SUSPENDED
            await asyncio.sleep(1)
            print("协程恢复")
            return "完成"

        # 创建协程对象(CREATED状态)
        coro = example_coroutine()
        print(f"协程对象: {coro}")
        print(f"协程状态: {inspect.getcoroutinestate(coro)}")  # CORO_CREATED

        # 运行协程
        task = asyncio.create_task(coro)
        result = await task

        print(f"协程结果: {result}")

# 协程对象的内部结构(CPython):
"""
typedef struct {
    PyObject_HEAD
    PyCodeObject *co_code;        // 代码对象
    PyObject *co_name;            // 协程名称
    PyFrameObject *co_frame;      // 执行帧
    char co_flags;                // 标志位
    PyObject *co_await;           // 当前等待的awaitable
    int co_running;               // 是否正在运行
    PyObject *co_origin;          // 创建位置
} PyCoroObject;

协程帧(Frame):
- 局部变量
- 执行栈
- 指令指针(下次恢复执行的位置)
- 返回值栈
"""

class CoroutineScheduling:
    """协程调度机制"""

    @staticmethod
    async def demonstrate_scheduling():
        """演示协程调度"""

        async def task_a():
            print("Task A 开始")
            for i in range(3):
                print(f"Task A step {i}")
                # yield控制权给事件循环
                await asyncio.sleep(0)
            print("Task A 完成")

        async def task_b():
            print("Task B 开始")
            for i in range(3):
                print(f"Task B step {i}")
                await asyncio.sleep(0)
            print("Task B 完成")

        # 并发执行两个任务
        await asyncio.gather(task_a(), task_b())

        # 输出顺序(协程调度):
        # Task A 开始
        # Task A step 0
        # Task B 开始
        # Task B step 0
        # Task A step 1
        # Task B step 1
        # Task A step 2
        # Task B step 2
        # Task A 完成
        # Task B 完成

# 协程调度的时间线:
"""
时间 →

Task A: [开始]─[step0]─[await]─────[恢复]─[step1]─[await]─────[恢复]─[step2]─[完成]
                       ↓yield                     ↓yield                ↓
Event Loop:           [切换]──────────────────[切换]──────────────────[切换]
                              ↓                        ↓
Task B:       [开始]────────[step0]─[await]─────[恢复]─[step1]─[await]─────[恢复]─[完成]

关键点:
1. await关键字让出控制权
2. 事件循环决定下一个执行哪个协程
3. 所有操作在单线程中,无需锁
4. 协作式多任务(非抢占式)
"""

3.3 async/await的底层实现

import sys
import types

class AsyncAwaitInternals:
    """async/await的底层原理"""

    @staticmethod
    def manual_coroutine_implementation():
        """手动实现协程(不使用async/await)"""

        # async def本质上是创建一个生成器
        # 但返回的是coroutine对象而不是generator对象

        # 手动实现一个简单的awaitable
        class Awaitable:
            def __await__(self):
                # __await__必须返回一个迭代器
                yield  # 让出控制权
                return "结果"

        # 手动实现协程
        def manual_coroutine():
            """
            这个函数等价于:
            async def manual_coroutine():
                result = await Awaitable()
                return result
            """
            # 创建awaitable
            awaitable = Awaitable()

            # 获取迭代器
            iterator = awaitable.__await__()

            # 驱动迭代器
            try:
                while True:
                    # send(None)相当于next()
                    iterator.send(None)
            except StopIteration as e:
                # 返回值在StopIteration的value中
                result = e.value
                return result

        # 实际的async def编译过程:
        """
        源代码:
        async def foo():
            x = await bar()
            return x

        编译后(简化):
        def foo():
            # 创建协程对象
            coro = PyCoroObject()
            coro.code = <foo的字节码>

            # 设置协程标志
            coro.flags = CO_COROUTINE | CO_ITERABLE_COROUTINE

            return coro

        执行await时的字节码:
        LOAD_NAME    bar
        CALL_FUNCTION 0
        GET_AWAITABLE         # 获取awaitable对象
        LOAD_CONST None
        YIELD_FROM            # 委托给awaitable的迭代器
        STORE_NAME x
        """

# async/await的类型层次:
"""
Coroutine (协程)
    ├─ 实现 __await__() 方法
    └─ 返回迭代器

Awaitable (可等待对象)
    ├─ Coroutine
    ├─ Task
    └─ Future

Generator (生成器)
    ├─ 实现 __iter__() 和 __next__()
    └─ 使用 yield

关系:
- async def 创建 Coroutine
- await 只能用于 Awaitable
- Coroutine 是特殊的 Generator
- yield from 可以委托给 Generator
- await 本质上是 yield from 的语法糖(但类型检查更严格)
"""

class FutureAndTask:
    """Future和Task的内部实现"""

    @staticmethod
    def demonstrate_future_internals():
        """演示Future的内部机制"""

        class SimpleFuture:
            """简化的Future实现"""

            def __init__(self, loop=None):
                self._loop = loop or asyncio.get_event_loop()
                self._state = 'PENDING'  # PENDING, CANCELLED, FINISHED
                self._result = None
                self._exception = None
                self._callbacks = []  # 完成时的回调列表

            def set_result(self, result):
                """设置结果"""
                if self._state != 'PENDING':
                    raise RuntimeError('Future已完成')

                self._result = result
                self._state = 'FINISHED'
                self._schedule_callbacks()

            def set_exception(self, exception):
                """设置异常"""
                if self._state != 'PENDING':
                    raise RuntimeError('Future已完成')

                self._exception = exception
                self._state = 'FINISHED'
                self._schedule_callbacks()

            def add_done_callback(self, callback):
                """添加完成回调"""
                if self._state == 'FINISHED':
                    # 已完成,立即调度回调
                    self._loop.call_soon(callback, self)
                else:
                    self._callbacks.append(callback)

            def _schedule_callbacks(self):
                """调度所有回调"""
                for callback in self._callbacks:
                    self._loop.call_soon(callback, self)
                self._callbacks.clear()

            def __await__(self):
                """实现awaitable协议"""
                if self._state == 'FINISHED':
                    if self._exception:
                        raise self._exception
                    return self._result

                # 让出控制权,等待结果
                yield self  # 返回Future自己给事件循环

                # 恢复时检查结果
                if self._exception:
                    raise self._exception
                return self._result

        # Task是Future的子类
        class SimpleTask(SimpleFuture):
            """简化的Task实现"""

            def __init__(self, coro, loop=None):
                super().__init__(loop)
                self._coro = coro  # 要执行的协程
                self._loop.call_soon(self._step)  # 立即调度第一步

            def _step(self, exc=None):
                """执行协程的一步"""
                try:
                    if exc is None:
                        # 恢复协程执行
                        result = self._coro.send(None)
                    else:
                        # 抛出异常到协程
                        result = self._coro.throw(exc)

                    # 协程返回了一个Future,等待它完成
                    if isinstance(result, SimpleFuture):
                        result.add_done_callback(self._wakeup)
                    else:
                        # 继续执行
                        self._loop.call_soon(self._step)

                except StopIteration as e:
                    # 协程完成
                    self.set_result(e.value)
                except Exception as e:
                    # 协程抛出异常
                    self.set_exception(e)

            def _wakeup(self, future):
                """被等待的Future完成时调用"""
                try:
                    result = future._result
                    self._step()
                except Exception as e:
                    self._step(e)

# Future和Task的关系:
"""
Future: 代表一个尚未完成的操作
    ├─ _state: 状态(PENDING/FINISHED/CANCELLED)
    ├─ _result: 结果
    ├─ _exception: 异常
    ├─ _callbacks: 回调列表
    └─ 方法: set_result(), set_exception(), add_done_callback()

Task: Future的子类,包装一个协程
    ├─ 继承Future的所有特性
    ├─ _coro: 被包装的协程
    ├─ _step(): 驱动协程执行
    └─ _wakeup(): Future完成时恢复协程

工作流程:
1. Task创建时调度_step()
2. _step()执行协程直到遇到await
3. await返回一个Future
4. 将_wakeup()注册为Future的回调
5. 当Future完成时,_wakeup()被调用
6. _wakeup()再次调用_step()
7. 重复直到协程完成
"""

3.4 I/O多路复用的底层实现

import select
import socket
import asyncio

class IOMultiplexing:
    """I/O多路复用原理"""

    @staticmethod
    def demonstrate_select():
        """演示select的使用"""

        # 创建服务器socket
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setblocking(False)
        server.bind(('localhost', 8888))
        server.listen(5)

        # 要监听的socket列表
        inputs = [server]
        outputs = []

        print("服务器启动,使用select监听...")

        while inputs:
            # select系统调用
            # 参数:读列表,写列表,异常列表,超时
            # 返回:就绪的读列表,就绪的写列表,异常列表
            readable, writable, exceptional = select.select(
                inputs, outputs, inputs, 1.0
            )

            # 处理可读socket
            for s in readable:
                if s is server:
                    # 接受新连接
                    client, addr = s.accept()
                    client.setblocking(False)
                    inputs.append(client)
                    print(f"新连接: {addr}")
                else:
                    # 读取数据
                    data = s.recv(1024)
                    if data:
                        print(f"收到数据: {data}")
                        outputs.append(s)
                    else:
                        # 连接关闭
                        inputs.remove(s)
                        s.close()

            # 处理可写socket
            for s in writable:
                s.send(b"Echo: OK\n")
                outputs.remove(s)

            # 处理异常
            for s in exceptional:
                inputs.remove(s)
                s.close()

# select/epoll/kqueue 对比:
"""
1. select (所有平台)
   原理:
   - 将要监听的fd_set传递给内核
   - 内核遍历所有文件描述符,检查是否就绪
   - 返回就绪的文件描述符集合

   伪代码:
   fd_set readfds;
   FD_ZERO(&readfds);
   FD_SET(socket_fd, &readfds);

   select(max_fd+1, &readfds, NULL, NULL, &timeout);

   if (FD_ISSET(socket_fd, &readfds)) {
       // socket可读
   }

   限制:
   ❌ fd数量限制(通常1024)
   ❌ 每次调用需要复制整个fd_set
   ❌ O(n)复杂度,遍历所有fd
   ❌ 性能随fd数量线性下降

2. poll (Unix/Linux)
   改进:
   ✅ 无fd数量限制
   ❌ 仍然是O(n)复杂度

   struct pollfd fds[2];
   fds[0].fd = socket1;
   fds[0].events = POLLIN;  // 监听可读

   poll(fds, 2, timeout);

3. epoll (Linux)
   原理:
   - 在内核中维护一个红黑树存储fd
   - 使用事件驱动,而不是轮询
   - 就绪的fd放入就绪列表

   伪代码:
   epfd = epoll_create(10);

   struct epoll_event ev;
   ev.events = EPOLLIN;
   ev.data.fd = socket_fd;
   epoll_ctl(epfd, EPOLL_CTL_ADD, socket_fd, &ev);

   struct epoll_event events[10];
   nfds = epoll_wait(epfd, events, 10, timeout);

   for (i = 0; i < nfds; i++) {
       // 处理events[i]
   }

   优势:
   ✅ O(1)复杂度(只返回就绪的fd)
   ✅ 无fd数量限制
   ✅ 边缘触发和水平触发模式
   ✅ 性能不随fd数量下降

4. kqueue (BSD/macOS)
   类似epoll,BSD系统的实现

   kq = kqueue();

   struct kevent ev;
   EV_SET(&ev, socket_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
   kevent(kq, &ev, 1, NULL, 0, NULL);

   struct kevent events[10];
   nfds = kevent(kq, NULL, 0, events, 10, &timeout);
"""

class AsyncIOImplementation:
    """asyncio的I/O实现"""

    @staticmethod
    async def demonstrate_asyncio_io():
        """演示asyncio的I/O处理"""

        # asyncio在不同平台选择不同的selector
        # Linux: epoll
        # Windows: IOCP (select)
        # macOS: kqueue

        async def fetch_url(url):
            # 异步HTTP请求的内部流程:

            # 1. 创建socket
            # sock = socket.socket()
            # sock.setblocking(False)

            # 2. 连接(非阻塞)
            # try:
            #     sock.connect(addr)
            # except BlockingIOError:
            #     pass  # 连接中

            # 3. 注册到selector,等待连接完成
            # selector.register(sock, EVENT_WRITE, callback)

            # 4. 事件循环调用selector.select()
            # events = selector.select(timeout)

            # 5. socket可写时,连接完成
            # callback被调用,恢复协程

            # 6. 发送请求
            # await sock.sendall(request)

            # 7. 注册读事件,等待响应
            # selector.modify(sock, EVENT_READ, callback)

            # 8. socket可读时,接收数据
            # data = await sock.recv(4096)

            reader, writer = await asyncio.open_connection('httpbin.org', 80)

            request = b'GET /get HTTP/1.1\r\nHost: httpbin.org\r\n\r\n'
            writer.write(request)
            await writer.drain()  # 等待发送完成

            response = await reader.read(1024)
            print(f"收到响应: {len(response)} 字节")

            writer.close()
            await writer.wait_closed()

# asyncio的完整I/O栈:
"""
应用层
    ↓
async/await 协程
    ↓
asyncio.Task
    ↓
asyncio.Future
    ↓
asyncio.EventLoop
    ↓
selectors.Selector (epoll/kqueue/select)
    ↓
操作系统内核
    ↓
网络/磁盘驱动
    ↓
硬件设备

数据流向:
1. 应用发起I/O操作(如read)
2. 创建Future对象
3. 注册fd到selector
4. 协程yield,让出控制权
5. 事件循环调用selector.select()等待
6. fd就绪时,selector返回
7. 回调被调用,设置Future结果
8. 协程被恢复
9. 返回数据给应用

零拷贝技术:
某些场景下(如sendfile),数据直接在内核空间传输
不需要复制到用户空间,大幅提升性能
"""

六、为什么能实现并发与并行?底层原理深度剖析

6.1 问题的本质:什么是并发与并行?

在深入探讨之前,我们需要理解一个核心问题:为什么单个CPU核心在同一时刻只能执行一条指令,却能让我们感觉多个任务在"同时"运行?

6.1.1 CPU的物理限制

┌─────────────────────────────────────────────────────────────┐
│                    单核CPU的执行单元                          │
│                                                               │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐              │
│  │   取指   │───▶│   译码   │───▶│   执行   │              │
│  └──────────┘    └──────────┘    └──────────┘              │
│                                                               │
│  在任意时刻t,只能执行一条机器指令                             │
│  物理限制:ALU(算术逻辑单元)同一时刻只能处理一个操作           │
└─────────────────────────────────────────────────────────────┘

关键认知:

  • 物理事实: 单个CPU核心在纳秒级别的任意时刻,只能执行一条指令
  • 并发的本质: 通过时间分片快速切换,让多个任务轮流使用CPU
  • 并行的本质: 拥有多个物理执行单元(多核),真正同时执行多个任务

6.2 进程为什么能实现并行与并发?

6.2.1 操作系统层面的根本原因

进程能实现并发/并行,源于操作系统的进程调度机制硬件的多核支持

// Linux内核中的进程调度核心数据结构
struct task_struct {
    // 进程状态
    volatile long state;  // TASK_RUNNING, TASK_INTERRUPTIBLE, etc.

    // CPU上下文(寄存器快照)
    struct thread_struct thread;  // 保存CPU寄存器值

    // 内存管理
    struct mm_struct *mm;  // 独立的虚拟地址空间

    // 调度信息
    int prio;              // 优先级
    unsigned int policy;   // 调度策略
    unsigned int time_slice; // 时间片

    // 进程关系
    struct task_struct *parent;
    struct list_head children;
};

为什么进程能并发执行?

  1. 时间分片机制:
时间轴: |--P1--|--P2--|--P3--|--P1--|--P2--|--P3--|...
        0     10ms   20ms   30ms   40ms   50ms   60ms

操作系统通过定时器中断(Timer Interrupt),每隔几毫秒强制进行进程切换:

1. 定时器中断触发(如每10ms)
2. 保存当前进程上下文(CPU寄存器、程序计数器等)
3. 选择下一个要运行的进程(调度算法)
4. 恢复目标进程的上下文
5. 跳转到目标进程继续执行
  1. 上下文切换的硬件支持:
; x86-64架构的上下文切换伪代码
context_switch:
    ; 保存当前进程的寄存器
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
    push rbp
    push r8-r15

    ; 保存当前栈指针到进程控制块
    mov [old_process.rsp], rsp

    ; 切换到新进程的栈
    mov rsp, [new_process.rsp]

    ; 恢复新进程的寄存器
    pop r15-r8
    pop rbp
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax

    ; 跳转到新进程
    ret

为什么进程能并行执行?

多核CPU架构:
┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   Core 0    │  │   Core 1    │  │   Core 2    │  │   Core 3    │
│  执行进程P1  │  │  执行进程P2  │  │  执行进程P3  │  │  执行进程P4  │
└─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘
       │                │                │                │
       └────────────────┴────────────────┴────────────────┘
                           共享L3缓存
                                │
                          ┌─────┴─────┐
                          │   主内存   │
                          └───────────┘

关键点:
1. 每个CPU核心有独立的执行单元(ALU、寄存器等)
2. 操作系统调度器将不同进程分配到不同核心
3. 多个核心真正同时执行不同进程的指令
4. 通过CPU亲和性(CPU Affinity)绑定进程到特定核心

6.2.2 Python多进程并行的实现

import os
import multiprocessing
import time

def cpu_bound_task(n):
    """CPU密集型任务"""
    print(f"进程 {os.getpid()} 在 CPU核心 {os.sched_getaffinity(0)} 上运行")
    result = sum(i * i for i in range(n))
    return result

if __name__ == "__main__":
    # 底层发生了什么?
    start = time.time()

    # 1. 创建进程池 - 调用fork()系统调用
    with multiprocessing.Pool(4) as pool:
        # 2. 每个子进程获得独立的:
        #    - 虚拟地址空间(mm_struct)
        #    - 文件描述符表(files_struct)
        #    - 信号处理表(signal_struct)
        #    - CPU上下文(thread_struct)

        # 3. 操作系统调度器将4个进程分配到4个CPU核心
        #    真正实现物理并行
        results = pool.map(cpu_bound_task, [10000000] * 4)

    print(f"耗时: {time.time() - start:.2f}秒")

    # 在4核CPU上,耗时约为单线程的1/4
    # 因为4个进程在4个核心上真正同时执行

为什么Python多进程能绕过GIL实现真并行?

进程间的内存隔离:

┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│   进程P1         │  │   进程P2         │  │   进程P3         │
│                  │  │                  │  │                  │
│  ┌───────────┐  │  │  ┌───────────┐  │  │  ┌───────────┐  │
│  │ Python解释│  │  │  │ Python解释│  │  │  │ Python解释│  │
│  │    器实例  │  │  │  │    器实例  │  │  │  │    器实例  │  │
│  │           │  │  │  │           │  │  │  │           │  │
│  │  GIL-1    │  │  │  │  GIL-2    │  │  │  │  GIL-3    │  │
│  └───────────┘  │  │  └───────────┘  │  │  └───────────┘  │
│                  │  │                  │  │                  │
│  虚拟地址空间1   │  │  虚拟地址空间2   │  │  虚拟地址空间3   │
└──────────────────┘  └──────────────────┘  └──────────────────┘

关键点:
1. 每个进程有独立的Python解释器实例
2. 每个解释器有自己的GIL,互不干扰
3. GIL只限制单个进程内的线程并行
4. 多个进程可以在多核上真正并行执行Python字节码

6.3 线程为什么能实现并发?

6.3.1 线程的本质:轻量级进程

// Linux内核中,线程实际上也是task_struct
// 但多个线程共享同一个进程的资源

// 进程P创建线程T的过程:
clone(CLONE_VM |      // 共享虚拟地址空间
      CLONE_FS |      // 共享文件系统信息
      CLONE_FILES |   // 共享文件描述符表
      CLONE_SIGHAND | // 共享信号处理
      CLONE_THREAD);  // 线程标志

// 结果:
// 线程T和进程P共享:
// - 代码段、数据段、堆
// - 全局变量
// - 文件描述符
// - 信号处理器

// 线程T独有:
// - 线程ID(TID)
// - 栈空间(thread_struct.sp指向独立的栈)
// - 寄存器上下文
// - 线程局部存储(TLS)

为什么线程能并发?

线程调度与进程调度本质相同:

时间轴视图:
|--T1--|--T2--|--T3--|--T1--|--T2--|--T3--|
0     5ms   10ms   15ms   20ms   25ms   30ms

底层机制:
1. 定时器中断(如每5ms)触发
2. 保存当前线程上下文(栈指针、寄存器)
3. 调度算法选择下一个线程
4. 恢复目标线程上下文
5. CPU继续执行目标线程

与进程切换的区别:
- 更快:不需要切换虚拟地址空间(不需要刷新TLB)
- 更轻:共享内存,上下文数据更少
- 开销:约5-10微秒(进程切换约20-100微秒)

6.3.2 为什么Python线程不能并行(GIL的限制)?

import threading
import sys

# GIL的实现原理(CPython源码简化)
class GIL:
    """GIL的简化模型"""
    def __init__(self):
        self.locked = 0  # 0=未锁定, 1=锁定
        self.switch_interval = 0.005  # 5毫秒切换间隔

    def acquire(self, thread_id):
        """线程获取GIL"""
        while True:
            # 原子操作:比较并交换
            if compare_and_swap(&self.locked, 0, 1):
                print(f"线程{thread_id}获得GIL")
                return
            else:
                # GIL被占用,等待信号
                wait_for_signal()

    def release(self, thread_id):
        """线程释放GIL"""
        print(f"线程{thread_id}释放GIL")
        self.locked = 0
        # 唤醒一个等待的线程
        signal_one_waiting_thread()

# Python字节码执行循环(PyEval_EvalFrameEx的简化版本)
def eval_frame(frame, gil):
    """执行Python字节码"""
    ticks = 0  # 执行的字节码指令计数

    while True:
        # 每执行一定数量的字节码指令就检查GIL
        if ticks >= 100:  # sys.getswitchinterval() * 1000
            # 释放GIL,给其他线程机会
            gil.release(current_thread_id())
            # 立即尝试重新获取GIL
            gil.acquire(current_thread_id())
            ticks = 0

        # 执行一条字节码指令
        opcode = frame.code[frame.pc]
        execute_opcode(opcode)

        ticks += 1

GIL导致的执行流程:

双核CPU上运行两个Python线程:

时间轴:
Core 0: |--T1(持有GIL)--|--T1等待--|--T1(持有GIL)--|
Core 1: |--T2等待GIL---|--T2(持有GIL)--|--T2等待--|

详细分解:
t=0ms:  线程T1获得GIL,在Core 0上执行Python字节码
        线程T2在Core 1上自旋等待GIL

t=5ms:  T1执行了100条字节码,检查到需要释放GIL
        T1释放GIL并立即尝试重新获取
        T2抢到GIL,开始在Core 1上执行
        T1在Core 0上等待GIL

t=10ms: T2释放GIL
        T1或T2抢到GIL,继续循环...

关键点:
1. 任意时刻,只有一个线程持有GIL
2. 只有持有GIL的线程能执行Python字节码
3. 即使有多个CPU核心,也只能有一个核心执行Python代码
4. 多线程在CPU密集型任务上甚至比单线程更慢(上下文切换开销)

线程在什么情况下能并发?

import threading
import time
import requests

def io_task():
    """I/O密集型任务"""
    response = requests.get("https://api.github.com")
    # 在等待网络响应期间:
    # 1. 线程主动释放GIL(调用了C扩展的阻塞函数)
    # 2. 操作系统将线程置于阻塞状态
    # 3. CPU调度其他线程执行
    # 4. 网络数据到达后,线程被唤醒并重新获取GIL
    return len(response.text)

# 虽然有GIL,但I/O等待期间会释放GIL,所以能并发
threads = [threading.Thread(target=io_task) for _ in range(10)]
for t in threads:
    t.start()
for t in threads:
    t.join()

6.4 协程为什么能实现并发?

6.4.1 协程的本质:用户态调度

传统线程(内核态调度):
┌──────────────────────────────────────────────┐
│            用户态(User Space)                 │
│  Thread1   Thread2   Thread3                 │
└──────────────┬───────────────────────────────┘
               │ 系统调用(System Call)
┌──────────────▼───────────────────────────────┐
│          内核态(Kernel Space)                 │
│      操作系统调度器(Scheduler)                 │
│  - 定时器中断                                  │
│  - 上下文切换                                  │
│  - 调度算法(CFS等)                            │
└──────────────────────────────────────────────┘

协程(用户态调度):
┌──────────────────────────────────────────────┐
│            用户态(User Space)                 │
│                                               │
│  ┌────────────────────────────────────┐      │
│  │      Event Loop(事件循环)          │      │
│  │    协程调度器(用户实现)             │      │
│  └────────────────────────────────────┘      │
│                                               │
│  Coroutine1  Coroutine2  Coroutine3          │
│     (挂起)     (运行)      (挂起)             │
└──────────────────────────────────────────────┘
         │ 只在I/O时才调用系统调用
         ▼
┌──────────────────────────────────────────────┐
│          内核态(Kernel Space)                 │
│      epoll/kqueue(I/O多路复用)               │
└──────────────────────────────────────────────┘

为什么协程能并发?

核心原理:协作式调度 + 异步I/O

# 协程的底层实现原理(简化版)

class Coroutine:
    """协程对象的内部结构"""
    def __init__(self, gen):
        self.gen = gen  # 生成器对象
        self.state = 'CREATED'  # CREATED, RUNNING, SUSPENDED, FINISHED
        self.stack = []  # 协程栈帧
        self.result = None

    def send(self, value):
        """恢复协程执行"""
        self.state = 'RUNNING'
        try:
            # 将值发送到生成器,恢复执行
            result = self.gen.send(value)
            self.state = 'SUSPENDED'
            return result
        except StopIteration as e:
            self.state = 'FINISHED'
            self.result = e.value
            raise

class EventLoop:
    """事件循环的核心机制"""
    def __init__(self):
        self.ready_queue = []  # 就绪队列
        self.waiting_io = {}   # 等待I/O的协程
        self.selector = selectors.DefaultSelector()  # I/O多路复用

    def create_task(self, coro):
        """创建任务"""
        task = Task(coro)
        self.ready_queue.append(task)
        return task

    def run_forever(self):
        """事件循环主逻辑"""
        while True:
            # 1. 执行所有就绪的协程
            while self.ready_queue:
                task = self.ready_queue.popleft()

                try:
                    # 执行协程直到下一个await点
                    result = task.coro.send(None)

                    # 如果协程await了一个I/O操作
                    if isinstance(result, IOWait):
                        # 注册到I/O多路复用器
                        self.selector.register(
                            result.fd,
                            result.events,
                            task
                        )
                        self.waiting_io[result.fd] = task
                    else:
                        # 协程主动yield,放回就绪队列
                        self.ready_queue.append(task)

                except StopIteration:
                    # 协程执行完毕
                    task.set_done()

            # 2. 等待I/O事件(非阻塞或短超时)
            events = self.selector.select(timeout=0)

            # 3. 处理I/O事件,唤醒等待的协程
            for key, mask in events:
                task = key.data
                self.selector.unregister(key.fd)
                del self.waiting_io[key.fd]
                # 将协程放回就绪队列
                self.ready_queue.append(task)

协程调度的时间线:

时间轴(单线程内):
|--C1执行--|--C2执行--|--C3执行--|--C1继续--|--C2继续--|

详细分解:
t=0ms:   协程C1开始执行,遇到 await read_socket()
         - C1主动挂起(yield),保存状态
         - 事件循环注册socket到epoll
         - 切换到协程C2(仅修改栈指针,无系统调用!)

t=1ms:   协程C2执行,遇到 await sleep(1)
         - C2主动挂起
         - 事件循环设置定时器
         - 切换到协程C3

t=2ms:   协程C3执行,遇到 await http_request()
         - C3主动挂起
         - 事件循环调用epoll_wait()检查I/O

t=10ms:  C1的socket有数据到达
         - epoll_wait()返回就绪事件
         - 事件循环恢复C1执行
         - C1读取数据,继续执行后续代码

t=1000ms: C2的定时器到期
          - 事件循环恢复C2执行

关键点:
1. 协程切换完全在用户态,无系统调用
2. 切换仅需保存/恢复栈指针和程序计数器
3. 开销极小:约0.1-0.5微秒(线程切换约5-10微秒)
4. 单线程内调度,无需锁,无GIL竞争

6.4.2 为什么协程能处理大规模并发?

import asyncio
import time

async def handle_client(client_id):
    """模拟处理一个客户端请求"""
    print(f"开始处理客户端 {client_id}")
    # 模拟I/O等待
    await asyncio.sleep(0.1)
    print(f"完成处理客户端 {client_id}")
    return f"结果{client_id}"

async def main():
    """同时处理10000个客户端"""
    tasks = [handle_client(i) for i in range(10000)]
    results = await asyncio.gather(*tasks)

# 运行
start = time.time()
asyncio.run(main())
print(f"耗时: {time.time() - start:.2f}秒")
# 输出约0.1秒,因为10000个协程并发等待I/O

# 内存占用分析:
# - 每个协程约2KB(栈帧)
# - 10000个协程约20MB
# - 如果用线程,每个约8MB(线程栈),10000个需要80GB!

为什么协程如此轻量?

线程 vs 协程的内存布局:

线程(由操作系统管理):
┌────────────────────────┐
│  Thread Control Block   │  约1KB
│  - TID                  │
│  - 调度信息             │
│  - 寄存器上下文         │
├────────────────────────┤
│    Thread Stack         │  默认8MB(Linux)
│  (预分配,防止溢出)      │
└────────────────────────┘
总计: ~8MB/线程

协程(由程序管理):
┌────────────────────────┐
│  Coroutine Object       │  约200B
│  - 状态                 │
│  - 栈指针               │
│  - 返回值               │
├────────────────────────┤
│  Stack Frame            │  按需增长,约2KB
│  (实际使用的栈空间)     │
└────────────────────────┘
总计: ~2KB/协程

差异原因:
1. 协程栈按需分配,线程栈预分配
2. 协程在用户态管理,无内核开销
3. 协程共享同一线程栈的地址空间

6.5 三者对比总结:为什么能实现并发/并行的根本原因

┌──────────────────────────────────────────────────────────────────┐
│                         实现并发/并行的本质                        │
└──────────────────────────────────────────────────────────────────┘

进程(Process):
├─ 并发原理: 操作系统时间分片 + 快速上下文切换
│           - 定时器中断强制切换
│           - 保存/恢复CPU寄存器和内存映射
│           - 调度算法选择下一个进程
│
└─ 并行原理: 多核CPU物理并行
            - 每个核心运行独立进程
            - 独立的地址空间,无共享冲突
            - Python多进程绕过GIL,真并行

线程(Thread):
├─ 并发原理: 与进程相同的时间分片机制
│           - 线程是轻量级进程
│           - 共享地址空间,切换更快
│           - Python线程受GIL限制,无法CPU并行
│
└─ 并行潜力: 原生线程可以并行(C/Java等)
            - Python线程因GIL无法CPU并行
            - I/O操作释放GIL,可以I/O并发

协程(Coroutine):
├─ 并发原理: 用户态协作式调度
│           - 主动yield让出CPU
│           - 事件循环统一调度
│           - 无系统调用,切换极快
│
└─ 无并行能力: 运行在单线程内
              - 无法利用多核
              - 适合I/O密集,不适合CPU密集
              - 需配合多进程实现CPU并行

6.5.1 深入到CPU指令级别的理解

单核CPU执行过程(纳秒级时间线):

t=0ns:    执行进程P1的指令: MOV eax, [0x1000]
t=2ns:    执行进程P1的指令: ADD eax, ebx
t=4ns:    执行进程P1的指令: MOV [0x1004], eax
...
t=10000000ns (10ms): 定时器中断触发!
          ├─ 硬件保存PC(程序计数器)到栈
          ├─ 跳转到中断处理程序
          ├─ 内核保存P1的所有寄存器
          ├─ 调度器选择进程P2
          ├─ 恢复P2的所有寄存器
          └─ 跳转到P2的PC位置

t=10000002ns: 执行进程P2的指令: MOV ecx, [0x2000]
...

关键洞察:
1. CPU没有"同时执行"的魔法,只是切换得极快
2. 并发 = 快速轮换 + 人类感知延迟(>50ms才能感知)
3. 并行 = 真正的多个CPU核心同时工作

6.5.2 为什么协程比线程快?

上下文切换开销对比:

线程切换(内核态):
1. 用户态陷入内核态 (SYSCALL指令)      ~100ns
2. 保存寄存器状态到线程控制块            ~50ns
3. 切换内存映射(如果是进程)             ~500ns
4. 刷新TLB(Translation Lookaside Buffer) ~200ns
5. 调度器选择下一个线程                 ~100ns
6. 恢复目标线程寄存器                   ~50ns
7. 从内核态返回用户态                   ~100ns
总计: ~5000-10000ns (5-10微秒)

协程切换(用户态):
1. 保存当前栈指针到协程对象              ~10ns
2. 更新协程状态标志                     ~5ns
3. 从事件循环选择下一个协程              ~20ns
4. 恢复目标协程栈指针                   ~10ns
5. 跳转到目标协程代码                   ~5ns
总计: ~100-500ns (0.1-0.5微秒)

速度差异: 协程比线程快10-100倍!

6.6 实战验证:测量切换开销

import threading
import asyncio
import time
import os

# 1. 测量线程切换开销
def measure_thread_switch():
    """测量线程上下文切换的开销"""
    switch_count = 1000000

    def thread_func(barrier, counter):
        for _ in range(switch_count):
            # 强制线程切换
            time.sleep(0)  # 触发系统调用,让出CPU
            counter[0] += 1

    counter = [0]
    barrier = threading.Barrier(2)

    t1 = threading.Thread(target=thread_func, args=(barrier, counter))
    t2 = threading.Thread(target=thread_func, args=(barrier, counter))

    start = time.perf_counter()
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    end = time.perf_counter()

    total_switches = counter[0]
    time_per_switch = (end - start) / total_switches * 1000000  # 微秒
    print(f"线程切换: {time_per_switch:.2f} 微秒/次")

# 2. 测量协程切换开销
async def measure_coroutine_switch():
    """测量协程切换的开销"""
    switch_count = 1000000
    counter = 0

    async def coro_func():
        nonlocal counter
        for _ in range(switch_count):
            await asyncio.sleep(0)  # 主动让出控制权
            counter += 1

    start = time.perf_counter()
    await asyncio.gather(coro_func(), coro_func())
    end = time.perf_counter()

    time_per_switch = (end - start) / counter * 1000000  # 微秒
    print(f"协程切换: {time_per_switch:.2f} 微秒/次")

# 3. 测量进程切换开销(间接测量)
def measure_process_switch():
    """测量进程切换的开销"""
    # 通过vmstat或perf工具间接测量
    import subprocess

    # 在Linux上使用perf
    if os.name == 'posix':
        result = subprocess.run(
            ['perf', 'stat', '-e', 'context-switches', 'sleep', '1'],
            capture_output=True,
            text=True
        )
        print("进程切换统计:")
        print(result.stderr)

# 运行测试
if __name__ == "__main__":
    print("="*50)
    print("上下文切换开销测量")
    print("="*50)

    measure_thread_switch()
    asyncio.run(measure_coroutine_switch())

    # 预期输出:
    # 线程切换: 5-10 微秒/次
    # 协程切换: 0.1-0.5 微秒/次

6.7 终极总结:选择的智慧

"""
并发/并行的本质与选择策略
"""

# 1. CPU密集型: 需要真并行
def cpu_intensive():
    # 为什么用多进程?
    # - 需要多核同时计算
    # - 每个进程独立GIL
    # - 真正的并行执行
    from multiprocessing import Pool
    with Pool(4) as p:
        result = p.map(heavy_computation, data)

# 2. I/O密集型: 需要高并发
async def io_intensive():
    # 为什么用协程?
    # - I/O等待期间可以处理其他任务
    # - 轻量级,可以创建数万个
    # - 事件驱动,响应快
    tasks = [fetch_url(url) for url in urls]
    results = await asyncio.gather(*tasks)

# 3. 混合负载: 需要组合
def hybrid_workload():
    # 进程池 + 协程
    # - 每个进程处理一部分数据(并行)
    # - 进程内用协程处理I/O(并发)
    from concurrent.futures import ProcessPoolExecutor

    async def process_partition(data):
        async with aiohttp.ClientSession() as session:
            tasks = [fetch_and_process(session, item) for item in data]
            return await asyncio.gather(*tasks)

    with ProcessPoolExecutor(4) as executor:
        futures = [
            executor.submit(asyncio.run, process_partition(partition))
            for partition in data_partitions
        ]

记住核心原则:

  1. 并发的本质: 时间分片 + 快速切换,让多个任务轮流执行
  2. 并行的本质: 多个物理执行单元同时工作
  3. 进程: 操作系统调度,独立地址空间,可真并行
  4. 线程: 操作系统调度,共享地址空间,Python受GIL限制
  5. 协程: 用户态调度,极轻量级,适合I/O密集
  6. 选择依据: 任务类型(CPU/IO) + 规模 + 资源限制

一句话总结:

并发是软件设计,并行是硬件能力。进程、线程、协程提供了从硬件到软件的不同抽象层次,让我们能充分利用计算资源,实现高效的并发与并行处理。

七、实战场景与选择策略

决策树

任务类型?
│
├─ CPU密集型(计算为主)
│  │
│  ├─ 需要真正并行?
│  │  ├─ 是 → 使用多进程 (multiprocessing)
│  │  └─ 否 → 串行执行或优化算法
│  │
│  └─ 数据量大?
│     ├─ 是 → 考虑numpy/numba等优化库
│     └─ 否 → 多进程 + 进程池
│
└─ I/O密集型(等待为主)
   │
   ├─ 并发数量?
   │  ├─ 少(<100) → 多线程 (threading)
   │  ├─ 中(100-1000) → 协程 (asyncio)
   │  └─ 多(>1000) → 协程 (asyncio)
   │
   └─ 是否需要共享状态?
      ├─ 是 → 多线程 + 锁
      └─ 否 → 协程(更简单)

实战场景示例

场景1:Web爬虫

import asyncio
import aiohttp
from bs4 import BeautifulSoup
import time

class AsyncWebCrawler:
    """异步网络爬虫"""

    def __init__(self, max_concurrent=10):
        self.max_concurrent = max_concurrent
        self.semaphore = asyncio.Semaphore(max_concurrent)
        self.session = None

    async def fetch(self, url):
        """获取单个URL"""
        async with self.semaphore:
            try:
                async with self.session.get(url, timeout=10) as response:
                    html = await response.text()
                    return url, html
            except Exception as e:
                print(f"Error fetching {url}: {e}")
                return url, None

    async def parse(self, url, html):
        """解析HTML"""
        if not html:
            return []

        soup = BeautifulSoup(html, 'html.parser')
        links = []
        for link in soup.find_all('a'):
            href = link.get('href')
            if href:
                links.append(href)
        return links

    async def crawl(self, start_urls, max_pages=100):
        """爬取网站"""
        visited = set()
        to_visit = set(start_urls)
        results = []

        async with aiohttp.ClientSession() as session:
            self.session = session

            while to_visit and len(visited) < max_pages:
                # 批量获取当前待访问的URL
                current_batch = list(to_visit)[:self.max_concurrent]
                to_visit -= set(current_batch)

                # 并发获取
                fetch_tasks = [self.fetch(url) for url in current_batch]
                pages = await asyncio.gather(*fetch_tasks)

                # 解析并提取新链接
                for url, html in pages:
                    if html:
                        visited.add(url)
                        links = await self.parse(url, html)

                        # 添加新发现的链接
                        new_links = set(links) - visited - to_visit
                        to_visit.update(new_links)

                        results.append((url, len(links)))

        return results

# 使用示例
async def main():
    crawler = AsyncWebCrawler(max_concurrent=20)
    start_urls = ['https://example.com']

    start = time.time()
    results = await crawler.crawl(start_urls, max_pages=50)
    elapsed = time.time() - start

    print(f"爬取了 {len(results)} 个页面,耗时 {elapsed:.2f}秒")
    print(f"平均速度: {len(results)/elapsed:.2f} 页/秒")

# asyncio.run(main())

# 选择理由:
# ✅ I/O密集型(网络请求)
# ✅ 高并发需求(可能上千个URL)
# ✅ 协程开销小,适合大量并发
# ✅ 单线程,不需要锁

场景2:数据处理流水线

from multiprocessing import Process, Queue, Pool
import time
import json

class DataPipeline:
    """数据处理流水线"""

    @staticmethod
    def extract(data_source):
        """提取数据(I/O密集)"""
        with open(data_source, 'r') as f:
            return json.load(f)

    @staticmethod
    def transform(data):
        """转换数据(CPU密集)"""
        # 复杂的数据转换逻辑
        result = []
        for item in data:
            # 模拟CPU密集型计算
            processed = {
                'id': item['id'],
                'value': sum(i**2 for i in range(1000)),
                'transformed': item['value'] * 2
            }
            result.append(processed)
        return result

    @staticmethod
    def load(data, target):
        """加载数据(I/O密集)"""
        with open(target, 'w') as f:
            json.dump(data, f)

    def process_batch(self, batch_data, output_queue):
        """处理一批数据"""
        # 转换(CPU密集型)使用当前进程
        transformed = self.transform(batch_data)
        output_queue.put(transformed)

    def run_pipeline(self, input_file, output_file, num_workers=4):
        """运行流水线"""
        # 1. 提取数据
        print("提取数据...")
        all_data = self.extract(input_file)

        # 2. 分批处理(多进程)
        print(f"使用 {num_workers} 个进程转换数据...")
        batch_size = len(all_data) // num_workers
        batches = [
            all_data[i:i+batch_size]
            for i in range(0, len(all_data), batch_size)
        ]

        # 使用进程池
        with Pool(processes=num_workers) as pool:
            results = pool.map(self.transform, batches)

        # 3. 合并结果
        final_data = []
        for batch_result in results:
            final_data.extend(batch_result)

        # 4. 加载数据
        print("加载数据...")
        self.load(final_data, output_file)

        print(f"完成!处理了 {len(final_data)} 条记录")

# 选择理由:
# ✅ Extract/Load是I/O密集型,可以串行或少量并发
# ✅ Transform是CPU密集型,使用多进程并行
# ✅ 混合型任务,分而治之

场景3:实时数据处理

import asyncio
import aioredis
from typing import List, Dict

class RealtimeDataProcessor:
    """实时数据处理系统"""

    def __init__(self):
        self.redis = None
        self.handlers = []

    async def connect(self):
        """连接到Redis"""
        self.redis = await aioredis.create_redis_pool('redis://localhost')

    async def subscribe_channel(self, channel: str):
        """订阅频道"""
        channels = await self.redis.subscribe(channel)
        return channels[0]

    async def process_message(self, message: Dict):
        """处理单条消息"""
        # 快速处理,不阻塞
        data = message.get('data')

        # 异步保存到数据库
        await self.save_to_db(data)

        # 异步推送到其他服务
        await self.push_to_service(data)

        # 触发回调
        for handler in self.handlers:
            await handler(data)

    async def save_to_db(self, data):
        """异步保存到数据库"""
        # 模拟数据库操作
        await asyncio.sleep(0.01)

    async def push_to_service(self, data):
        """推送到其他服务"""
        # 模拟API调用
        await asyncio.sleep(0.02)

    async def run(self, channels: List[str]):
        """运行处理器"""
        await self.connect()

        # 订阅多个频道
        channel_objects = await asyncio.gather(*[
            self.subscribe_channel(ch) for ch in channels
        ])

        # 并发处理所有频道的消息
        async def listen_channel(channel):
            while True:
                message = await channel.get()
                if message:
                    # 不等待处理完成,继续接收下一条
                    asyncio.create_task(self.process_message(message))

        # 并发监听所有频道
        await asyncio.gather(*[
            listen_channel(ch) for ch in channel_objects
        ])

# 选择理由:
# ✅ 高并发消息处理
# ✅ I/O密集型(网络、数据库)
# ✅ 需要快速响应
# ✅ 协程最适合

场景4:图像批量处理

from multiprocessing import Pool, cpu_count
from PIL import Image
import os

class ImageProcessor:
    """图像批量处理器"""

    @staticmethod
    def process_single_image(args):
        """处理单张图片(CPU密集型)"""
        input_path, output_path, operations = args

        try:
            # 打开图片
            img = Image.open(input_path)

            # 应用操作
            for op in operations:
                if op['type'] == 'resize':
                    img = img.resize(op['size'])
                elif op['type'] == 'rotate':
                    img = img.rotate(op['angle'])
                elif op['type'] == 'filter':
                    # 应用滤镜(CPU密集)
                    img = img.filter(op['filter'])

            # 保存
            img.save(output_path)
            return True, input_path
        except Exception as e:
            return False, f"Error processing {input_path}: {e}"

    def process_batch(self, image_paths: List[str], operations: List[Dict],
                     output_dir: str, num_workers: int = None):
        """批量处理图片"""
        if num_workers is None:
            num_workers = cpu_count()

        # 准备参数
        tasks = []
        for path in image_paths:
            filename = os.path.basename(path)
            output_path = os.path.join(output_dir, filename)
            tasks.append((path, output_path, operations))

        # 使用进程池并行处理
        print(f"使用 {num_workers} 个进程处理 {len(tasks)} 张图片...")

        with Pool(processes=num_workers) as pool:
            results = pool.map(self.process_single_image, tasks)

        # 统计结果
        success = sum(1 for r in results if r[0])
        failed = len(results) - success

        print(f"成功: {success}, 失败: {failed}")
        return results

# 使用示例
processor = ImageProcessor()

operations = [
    {'type': 'resize', 'size': (800, 600)},
    {'type': 'rotate', 'angle': 90},
]

image_paths = ['img1.jpg', 'img2.jpg', 'img3.jpg']  # 假设有很多图片
processor.process_batch(image_paths, operations, 'output/')

# 选择理由:
# ✅ CPU密集型(图像处理)
# ✅ 可以完全并行
# ✅ 使用多进程充分利用多核
# ✅ 每个任务独立,无需通信

八、最佳实践

1. 任务类型识别

import time
import psutil
import os

class TaskProfiler:
    """任务性能分析器"""

    @staticmethod
    def profile_task(func, *args, **kwargs):
        """分析任务类型"""
        process = psutil.Process(os.getpid())

        # 记录初始状态
        cpu_before = process.cpu_percent(interval=0.1)
        io_before = process.io_counters()

        # 执行任务
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start

        # 记录最终状态
        cpu_after = process.cpu_percent(interval=0.1)
        io_after = process.io_counters()

        # 计算I/O量
        io_read = io_after.read_bytes - io_before.read_bytes
        io_write = io_after.write_bytes - io_before.write_bytes

        # 判断类型
        avg_cpu = (cpu_before + cpu_after) / 2
        total_io = io_read + io_write

        if avg_cpu > 70:
            task_type = "CPU密集型"
            recommendation = "使用多进程(multiprocessing)"
        elif total_io > 1024 * 1024:  # > 1MB
            task_type = "I/O密集型"
            recommendation = "使用协程(asyncio)或多线程"
        else:
            task_type = "混合型"
            recommendation = "根据主要瓶颈选择"

        print(f"\n任务分析结果:")
        print(f"执行时间: {elapsed:.2f}秒")
        print(f"CPU使用: {avg_cpu:.1f}%")
        print(f"I/O读取: {io_read / 1024:.2f} KB")
        print(f"I/O写入: {io_write / 1024:.2f} KB")
        print(f"任务类型: {task_type}")
        print(f"建议方案: {recommendation}")

        return result

# 使用示例
def my_task():
    # 你的任务代码
    result = sum(i**2 for i in range(1000000))
    return result

TaskProfiler.profile_task(my_task)

2. 混合任务处理策略

import asyncio
from concurrent.futures import ProcessPoolExecutor
import aiohttp

class HybridTaskExecutor:
    """混合任务执行器"""

    def __init__(self, max_processes=4):
        self.process_executor = ProcessPoolExecutor(max_workers=max_processes)

    async def execute_hybrid_pipeline(self, data_sources):
        """执行混合流水线"""
        results = []

        # 步骤1:并发获取数据(I/O密集)
        async with aiohttp.ClientSession() as session:
            fetch_tasks = [
                self.fetch_data(session, source)
                for source in data_sources
            ]
            raw_data = await asyncio.gather(*fetch_tasks)

        # 步骤2:并行处理数据(CPU密集)
        loop = asyncio.get_event_loop()
        process_tasks = [
            loop.run_in_executor(self.process_executor, self.process_data, data)
            for data in raw_data
        ]
        processed_data = await asyncio.gather(*process_tasks)

        # 步骤3:并发保存结果(I/O密集)
        save_tasks = [
            self.save_result(result)
            for result in processed_data
        ]
        await asyncio.gather(*save_tasks)

        return processed_data

    async def fetch_data(self, session, source):
        """获取数据(I/O密集 - 协程)"""
        async with session.get(source) as response:
            return await response.json()

    @staticmethod
    def process_data(data):
        """处理数据(CPU密集 - 进程)"""
        # 复杂计算
        return {
            'processed': sum(item**2 for item in range(100000)),
            'original': data
        }

    async def save_result(self, result):
        """保存结果(I/O密集 - 协程)"""
        await asyncio.sleep(0.1)  # 模拟I/O
        return True

    def __del__(self):
        self.process_executor.shutdown()

# 使用
async def main():
    executor = HybridTaskExecutor(max_processes=4)
    sources = ['http://api1.com/data', 'http://api2.com/data']
    results = await executor.execute_hybrid_pipeline(sources)

# asyncio.run(main())

3. 性能监控与优化

import time
import functools
from typing import Callable, Any
import asyncio

def performance_monitor(task_type: str = 'unknown'):
    """性能监控装饰器"""
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs) -> Any:
            start = time.time()
            try:
                result = await func(*args, **kwargs)
                elapsed = time.time() - start
                print(f"[{task_type}] {func.__name__} 耗时: {elapsed:.3f}秒")
                return result
            except Exception as e:
                elapsed = time.time() - start
                print(f"[{task_type}] {func.__name__} 失败: {e}, 耗时: {elapsed:.3f}秒")
                raise

        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs) -> Any:
            start = time.time()
            try:
                result = func(*args, **kwargs)
                elapsed = time.time() - start
                print(f"[{task_type}] {func.__name__} 耗时: {elapsed:.3f}秒")
                return result
            except Exception as e:
                elapsed = time.time() - start
                print(f"[{task_type}] {func.__name__} 失败: {e}, 耗时: {elapsed:.3f}秒")
                raise

        return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper

    return decorator

# 使用示例
@performance_monitor(task_type='I/O')
async def fetch_data(url):
    await asyncio.sleep(1)
    return "data"

@performance_monitor(task_type='CPU')
def compute_result(n):
    return sum(i**2 for i in range(n))

4. 常见陷阱与解决方案

# 陷阱1:在异步函数中使用同步I/O
# ❌ 错误示例
async def bad_async():
    import time
    time.sleep(1)  # 阻塞整个事件循环!
    return "done"

# ✅ 正确示例
async def good_async():
    await asyncio.sleep(1)  # 非阻塞
    return "done"

# 陷阱2:在多线程中修改共享状态不加锁
# ❌ 错误示例
counter = 0

def bad_thread_func():
    global counter
    for _ in range(100000):
        counter += 1  # 竞态条件!

# ✅ 正确示例
from threading import Lock

counter = 0
lock = Lock()

def good_thread_func():
    global counter
    for _ in range(100000):
        with lock:
            counter += 1

# 陷阱3:创建过多进程
# ❌ 错误示例
from multiprocessing import Process

processes = []
for i in range(1000):  # 太多进程!
    p = Process(target=some_func)
    processes.append(p)
    p.start()

# ✅ 正确示例
from multiprocessing import Pool

with Pool(processes=cpu_count()) as pool:
    results = pool.map(some_func, range(1000))

# 陷阱4:忘记join进程/线程
# ❌ 错误示例
import threading

t = threading.Thread(target=some_func)
t.start()
# 主程序可能在线程完成前就退出

# ✅ 正确示例
t = threading.Thread(target=some_func)
t.start()
t.join()  # 等待线程完成

九、总结

快速决策表

场景CPU使用I/O等待并发数推荐方案原因
Web爬虫高(>1000)协程I/O密集,高并发
数据分析多进程CPU密集,需要并行
API服务协程I/O密集,快速响应
图像处理多进程CPU密集,可并行
文件处理多线程I/O密集,简单
机器学习训练-多进程+GPUCPU密集,大计算量
实时消息极高协程I/O密集,超高并发

记忆口诀

CPU密集用进程,多核并行真给力
I/O密集用协程,千万并发不是梦
线程适合小并发,共享内存很方便
混合任务需组合,流水线式最高效

GIL影响要记牢,线程计算不给力
进程隔离更安全,通信代价要考虑
协程轻量启动快,单线程内来调度
选对方案事半功,性能提升看得见

最后建议

  1. 先分析后选择:使用性能分析工具确定瓶颈
  2. 从简单开始:能串行就串行,必要时才并发
  3. 测量再优化:不要过早优化,用数据说话
  4. 考虑维护性:复杂的并发代码难以调试
  5. 关注资源:并发不是越多越好,要考虑系统资源

记住:没有银弹,只有最适合的方案!