Python编程:并发编程

并发编程是现代软件开发中提高程序性能的重要手段。Python提供了多种并发编程方式,包括多线程、多进程和异步IO。

基本概念

1 并发 vs 并行

  • 并发(Concurrency): 多个任务交替执行,看起来像是同时运行

  • 并行(Parallelism): 多个任务真正同时执行,需要多核CPU支持

2 Python的并发模型

  • I/O密集型任务: 适合使用多线程或异步IO

  • CPU密集型任务: 适合使用多进程

多线程编程

Python通过threading模块提供线程支持,但由于GIL(全局解释器锁)的存在,多线程不适合CPU密集型任务。

1 基本使用

import threading
import time

def task(name):
    print(f"任务 {name} 开始")
    time.sleep(2)  # 模拟I/O操作
    print(f"任务 {name} 完成")

# 创建线程
threads = []
for i in range(3):
    t = threading.Thread(target=task, args=(f"Thread-{i}",))
    threads.append(t)
    t.start()

# 等待所有线程完成
for t in threads:
    t.join()

print("所有任务完成")

2 线程同步

# 使用锁
counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        with lock:  # 自动获取和释放锁
            counter += 1

threads = []
for _ in range(5):
    t = threading.Thread(target=increment)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print(f"最终计数器值: {counter}")  # 应该是500000

# 使用信号量
semaphore = threading.Semaphore(3)  # 最多3个线程同时访问

def limited_resource(user):
    with semaphore:
        print(f"{user} 正在使用资源")
        time.sleep(1)
        print(f"{user} 释放资源")

for i in range(10):
    threading.Thread(target=limited_resource, args=(f"User-{i}",)).start()

3 线程间通信

# 使用队列
import queue

def producer(q):
    for i in range(5):
        print(f"生产物品 {i}")
        q.put(i)
        time.sleep(0.5)
    q.put(None)  # 结束信号

def consumer(q):
    while True:
        item = q.get()
        if item is None:
            break
        print(f"消费物品 {item}")
        time.sleep(1)

q = queue.Queue()
threading.Thread(target=producer, args=(q,)).start()
threading.Thread(target=consumer, args=(q,)).start()

多进程编程

多进程可以绕过GIL限制,适合CPU密集型任务,但进程间通信开销较大。

1 基本使用

from multiprocessing import Process
import os

def cpu_bound_task(n):
    print(f"进程 {os.getpid()} 计算 {n} 的平方")
    return n * n

if __name__ == '__main__':
    processes = []
    for i in range(4):
        p = Process(target=cpu_bound_task, args=(i,))
        processes.append(p)
        p.start()
    
    for p in processes:
        p.join()

2 进程池

from multiprocessing import Pool

def square(x):
    return x * x

if __name__ == '__main__':
    with Pool(4) as pool:  # 4个工作进程
        # map方法
        results = pool.map(square, range(10))
        print(results)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
        
        # apply_async方法
        result = pool.apply_async(square, (10,))
        print(result.get())  # 100

3 进程间通信

from multiprocessing import Process, Queue

def worker(q):
    while True:
        item = q.get()
        if item is None:
            break
        print(f"处理: {item}")

if __name__ == '__main__':
    q = Queue()
    p = Process(target=worker, args=(q,))
    p.start()
    
    for i in range(5):
        q.put(i)
    
    q.put(None)  # 结束信号
    p.join()

异步IO(asyncio)

asyncio是Python3.4引入的标准库,适合I/O密集型任务,使用单线程实现高并发。

1 基本概念

  • 协程(Coroutine): 使用async def定义的函数

  • 事件循环(Event Loop): 协程的调度器

  • Future/Task: 表示异步操作的结果

2 基本使用

import asyncio

async def fetch_data(url):
    print(f"开始获取 {url}")
    await asyncio.sleep(2)  # 模拟I/O操作
    print(f"完成获取 {url}")
    return f"{url} 的数据"

async def main():
    # 顺序执行
    result1 = await fetch_data("url1")
    result2 = await fetch_data("url2")
    print(result1, result2)
    
    # 并发执行
    task1 = asyncio.create_task(fetch_data("url3"))
    task2 = asyncio.create_task(fetch_data("url4"))
    await task1
    await task2
    
    # 使用gather
    results = await asyncio.gather(
        fetch_data("url5"),
        fetch_data("url6"),
        fetch_data("url7")
    )
    print(results)

asyncio.run(main())

3 高级特性

# 超时控制
async def slow_operation():
    await asyncio.sleep(5)
    return "完成"

async def main():
    try:
        result = await asyncio.wait_for(slow_operation(), timeout=3.0)
    except asyncio.TimeoutError:
        print("操作超时")

# 事件循环控制
async def periodic_task():
    while True:
        print("执行周期性任务")
        await asyncio.sleep(1)

async def main():
    task = asyncio.create_task(periodic_task())
    await asyncio.sleep(5)
    task.cancel()
    try:
        await task
    except asyncio.CancelledError:
        print("任务已取消")

并发编程选择指南

方法适用场景优点缺点
多线程I/O密集型,GUI应用轻量级,共享内存方便受GIL限制,不适合CPU密集型
多进程CPU密集型任务绕过GIL,利用多核内存开销大,进程间通信复杂
异步IO高并发I/O操作,网络应用高效,单线程高并发需要特殊库支持,学习曲线陡峭

实际应用示例

1 并发下载器

import aiohttp
import asyncio

async def download(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            content = await response.read()
            print(f"下载 {url} 完成,长度: {len(content)}")
            return content

async def main():
    urls = [
        'https://www.python.org',
        'https://www.google.com',
        'https://www.github.com'
    ]
    tasks = [download(url) for url in urls]
    await asyncio.gather(*tasks)

asyncio.run(main())

2 并行计算

from multiprocessing import Pool
import math

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True

if __name__ == '__main__':
    numbers = range(1000000, 1000100)
    with Pool(4) as pool:
        results = pool.map(is_prime, numbers)
        primes = [n for n, prime in zip(numbers, results) if prime]
        print(f"找到 {len(primes)} 个质数")

常见问题

  1. GIL限制

    • 使用多进程代替多线程处理CPU密集型任务

    • 使用C扩展释放GIL

  2. 死锁问题

    • 按固定顺序获取锁

    • 使用带超时的锁

  3. 资源竞争

    • 使用线程安全的数据结构

    • 尽量减少共享状态

  4. 协程阻塞

    • 避免在协程中使用阻塞I/O

    • 使用专门的异步库(aiohttp, asyncpg等)

通过合理选择并发模型并正确实现,可以显著提高Python程序的性能,特别是在处理I/O密集型或CPU密集型任务时。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值