Python多线程、多进程、协程、锁、同步、异步的详解和应用

Python 中的并发编程有多种方式,包括多线程、多进程、协程,以及与之相关的锁、同步和异步机制。下面将分别进行详解,并给出应用场景举例。

1. 多线程 (Threading)

多线程允许在同一个进程中运行多个线程,共享内存空间。由于全局解释器锁(GIL)的存在,Python 的多线程并不适合计算密集型任务,但适合 I/O 密集型任务。

详解:

  • 线程是操作系统能够进行运算调度的最小单位,被包含在进程之中。

  • 在 Python 中,threading 模块提供了线程相关的操作。

  • 由于 GIL,同一时刻只有一个线程可以执行 Python 字节码,因此多线程在 CPU 密集型任务中并不能并行执行,但在 I/O 密集型任务中,当线程等待 I/O 时,其他线程可以运行。

应用场景:

  • 网络请求(如下载多个文件)。

  • 处理多个客户端的服务器。

  • 任何有大量等待(I/O)的任务。

举例:

python

复制代码
import threading
import time

def download_file(url):
    print(f"开始下载 {url}")
    time.sleep(2)  # 模拟网络延迟
    print(f"下载完成 {url}")

urls = ["http://example.com/file1", "http://example.com/file2", "http://example.com/file3"]
threads = []

for url in urls:
    thread = threading.Thread(target=download_file, args=(url,))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

2. 多进程 (Multiprocessing)

多进程可以绕过 GIL,实现真正的并行计算,因为每个进程有自己独立的 GIL。适合计算密集型任务。

详解:

  • 进程是资源分配的最小单位,每个进程有独立的内存空间。

  • 使用 multiprocessing 模块可以创建进程。

  • 进程间通信(IPC)可以使用队列、管道等。

应用场景:

  • 计算密集型任务,如图像处理、数值计算等。

  • 需要充分利用多核 CPU 的任务。

举例:

python

复制代码
import multiprocessing
import time

def calculate_square(numbers):
    for n in numbers:
        time.sleep(0.01)  # 模拟计算耗时
        print(f"平方 {n}: {n*n}")

if __name__ == "__main__":
    numbers = list(range(10))
    # 创建两个进程
    p1 = multiprocessing.Process(target=calculate_square, args=(numbers[:5],))
    p2 = multiprocessing.Process(target=calculate_square, args=(numbers[5:],))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

3. 协程 (Coroutine)

协程是一种用户态的轻量级线程,由用户控制调度,开销远小于线程。在 Python 中,协程通常与 asyncio 库一起使用,用于异步编程。

详解:

  • 协程可以在同一个线程中实现并发,通过事件循环来调度。

  • 使用 asyncawait 关键字定义协程。

  • 适合 I/O 密集型任务,且可以处理大量并发连接。

应用场景:

  • 高并发的网络应用,如 Web 服务器。

  • 爬虫并发下载。

  • 实时数据处理。

举例:

python

复制代码
import asyncio
import aiohttp

async def fetch_url(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def main():
    urls = ["http://example.com", "http://example.org", "http://example.net"]
    tasks = [fetch_url(url) for url in urls]
    results = await asyncio.gather(*tasks)
    for url, content in zip(urls, results):
        print(f"{url}: 获取了 {len(content)} 字节")

asyncio.run(main())

4. 锁 (Lock)

在多线程或多进程中,当共享资源时,为了防止竞争条件,需要使用锁来确保同一时间只有一个线程或进程访问共享资源。

详解:

  • 锁有两种状态:锁定和未锁定。

  • 使用锁可以保证数据一致性,但过度使用会导致性能下降。

应用场景:

  • 多个线程或进程需要修改同一个共享变量。

  • 对文件或数据库的写操作。

举例(多线程中的锁):

python

复制代码
import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        lock.acquire()
        counter += 1
        lock.release()

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

for thread in threads:
    thread.join()

print(f"最终计数器值: {counter}")

5. 同步 (Synchronization)

同步机制用于协调多个线程或进程的执行顺序,确保它们按照一定的顺序或条件执行。除了锁,还有信号量、事件、条件变量等。

应用场景:

  • 生产者-消费者问题。

  • 限制同时访问资源的线程数。

举例(使用信号量):

python

复制代码
import threading
import time

semaphore = threading.Semaphore(3)  # 最多允许3个线程同时访问

def access_resource(thread_id):
    semaphore.acquire()
    print(f"线程 {thread_id} 正在访问资源")
    time.sleep(2)
    print(f"线程 {thread_id} 释放资源")
    semaphore.release()

threads = []
for i in range(10):
    thread = threading.Thread(target=access_resource, args=(i,))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

6. 异步 (Asynchronous)

异步编程允许程序在等待 I/O 操作时继续执行其他任务,从而提高效率。在 Python 中,异步编程通常使用 asyncio 库。

详解:

  • 异步编程基于事件循环和协程。

  • 使用 async 定义异步函数,await 来等待异步操作完成。

  • 适合 I/O 密集型且需要高并发的场景。

应用场景:

  • 高性能网络服务器(如 aiohttp)。

  • 异步数据库驱动。

  • 实时消息推送。

举例(异步文件读写,使用 aiofiles):

python

复制代码
import asyncio
import aiofiles

async def write_file(filename, content):
    async with aiofiles.open(filename, 'w') as f:
        await f.write(content)
        print(f"写入完成 {filename}")

async def main():
    tasks = []
    for i in range(5):
        tasks.append(write_file(f"file_{i}.txt", f"内容 {i}"))
    await asyncio.gather(*tasks)

asyncio.run(main())

总结

  • 多线程:适合 I/O 密集型任务,由于 GIL 的存在,不适合计算密集型任务。

  • 多进程:适合计算密集型任务,可以充分利用多核 CPU,但进程间通信开销较大。

  • 协程:适合高并发的 I/O 密集型任务,轻量级,由用户控制调度,通常与异步编程结合。

  • :用于解决多线程或多进程中的共享资源竞争问题。

  • 同步:协调多个线程或进程的执行顺序。

  • 异步:基于事件循环,提高 I/O 密集型任务的并发性能。

根据不同的任务类型和需求,选择合适的技术。

Python 并发编程详解

一、多线程 (Multithreading)

特点:

  • 共享内存:线程间共享进程的内存空间

  • 受GIL限制:Python全局解释器锁(GIL)限制同一时刻只有一个线程执行Python字节码

  • 适合I/O密集型任务:如网络请求、文件读写

  • 轻量级:创建和切换开销小

应用场景:

python

复制代码
import threading
import time
import requests

# 1. 多线程下载文件
def download_file(url, filename):
    response = requests.get(url)
    with open(filename, 'wb') as f:
        f.write(response.content)
    print(f"{filename} 下载完成")

# 场景:批量下载文件
urls = [('http://example.com/file1', 'file1.txt'),
        ('http://example.com/file2', 'file2.txt')]

threads = []
for url, filename in urls:
    t = threading.Thread(target=download_file, args=(url, filename))
    t.start()
    threads.append(t)

for t in threads:
    t.join()

# 2. Web服务器处理并发请求
class WebServer:
    def handle_request(self, client_id):
        time.sleep(0.5)  # 模拟处理时间
        print(f"处理客户端 {client_id} 的请求")
    
    def start(self):
        for i in range(10):
            t = threading.Thread(target=self.handle_request, args=(i,))
            t.start()

优势:

  • 快速响应用户界面

  • 适合I/O等待时间长的任务

  • 内存占用小

二、多进程 (Multiprocessing)

特点:

  • 独立内存空间:每个进程有独立的内存,需进程间通信(IPC)

  • 不受GIL限制:可真正并行执行CPU密集型任务

  • 开销较大:创建和切换开销比线程大

  • 适合CPU密集型任务

应用场景:

python

复制代码
import multiprocessing
import math
from multiprocessing import Pool

# 1. CPU密集型计算
def cpu_intensive_task(n):
    """计算质数个数"""
    count = 0
    for i in range(2, n):
        is_prime = True
        for j in range(2, int(math.sqrt(i)) + 1):
            if i % j == 0:
                is_prime = False
                break
        if is_prime:
            count += 1
    return count

# 场景:大数据计算
def process_demo():
    numbers = [100000, 150000, 200000, 250000]
    
    # 单进程
    start = time.time()
    results = [cpu_intensive_task(n) for n in numbers]
    print(f"单进程耗时: {time.time() - start}")
    
    # 多进程
    start = time.time()
    with Pool(processes=4) as pool:
        results = pool.map(cpu_intensive_task, numbers)
    print(f"多进程耗时: {time.time() - start}")

# 2. 生产者-消费者模式
def producer(queue, items):
    for item in items:
        queue.put(item)
        time.sleep(0.1)

def consumer(queue, name):
    while True:
        item = queue.get()
        if item is None:
            break
        print(f"{name} 处理: {item}")

def producer_consumer_demo():
    queue = multiprocessing.Queue()
    
    # 创建生产者
    items = [f"任务{i}" for i in range(20)]
    p1 = multiprocessing.Process(target=producer, args=(queue, items))
    
    # 创建消费者
    c1 = multiprocessing.Process(target=consumer, args=(queue, "消费者1"))
    c2 = multiprocessing.Process(target=consumer, args=(queue, "消费者2"))
    
    p1.start()
    c1.start()
    c2.start()
    
    p1.join()
    queue.put(None)  # 结束信号
    queue.put(None)
    
    c1.join()
    c2.join()

优势:

  • 充分利用多核CPU

  • 内存隔离更安全

  • 适合计算密集型任务

三、协程 (Coroutine)

特点:

  • 用户态调度:由程序控制切换,开销极小

  • 异步非阻塞:适合高并发I/O

  • 单线程内并发:在同一个线程内切换

  • Python 3.5+支持async/await语法

应用场景:

python

复制代码
import asyncio
import aiohttp

# 1. 高并发网络请求
async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()

async def async_demo():
    urls = [
        'http://httpbin.org/delay/1',
        'http://httpbin.org/delay/2',
        'http://httpbin.org/delay/1'
    ]
    
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for url, content in zip(urls, results):
            print(f"{url}: {len(content)} bytes")

# 2. WebSocket服务器
async def handle_client(reader, writer):
    addr = writer.get_extra_info('peername')
    print(f"客户端 {addr} 连接")
    
    while True:
        data = await reader.read(100)
        if not data:
            break
        message = data.decode()
        print(f"收到: {message}")
        writer.write(f"已收到: {message}".encode())
        await writer.drain()
    
    writer.close()

async def websocket_server():
    server = await asyncio.start_server(
        handle_client, '127.0.0.1', 8888)
    async with server:
        await server.serve_forever()

# 3. 生产者-消费者协程版
async def async_producer(queue, n):
    for i in range(n):
        await queue.put(f"产品{i}")
        await asyncio.sleep(0.1)
    await queue.put(None)

async def async_consumer(queue, name):
    while True:
        item = await queue.get()
        if item is None:
            break
        print(f"{name} 消费: {item}")
        await asyncio.sleep(0.2)

async def async_producer_consumer():
    queue = asyncio.Queue(maxsize=10)
    
    producer_task = asyncio.create_task(async_producer(queue, 20))
    consumer_tasks = [
        asyncio.create_task(async_consumer(queue, "消费者1")),
        asyncio.create_task(async_consumer(queue, "消费者2"))
    ]
    
    await producer_task
    await queue.put(None)  # 通知消费者结束
    await asyncio.gather(*consumer_tasks)

优势:

  • 极高的并发性能(可支持上万并发连接)

  • 极低的资源消耗

  • 代码结构清晰(类似同步代码)

四、锁与同步机制

1. 线程锁

python

复制代码
import threading

# 互斥锁
class BankAccount:
    def __init__(self, balance):
        self.balance = balance
        self.lock = threading.Lock()
    
    def transfer(self, amount):
        with self.lock:  # 自动获取和释放锁
            old_balance = self.balance
            self.balance += amount
            print(f"{old_balance} -> {self.balance}")

# 递归锁(可重入锁)
class RecursiveExample:
    def __init__(self):
        self.rlock = threading.RLock()
        self.value = 0
    
    def method1(self):
        with self.rlock:
            self.value += 1
            self.method2()  # 可以再次获取同一个锁
    
    def method2(self):
        with self.rlock:
            self.value -= 1

# 信号量
class ConnectionPool:
    def __init__(self, max_connections):
        self.semaphore = threading.Semaphore(max_connections)
    
    def get_connection(self):
        self.semaphore.acquire()
        # 获取连接
        return Connection()
    
    def release_connection(self, conn):
        # 释放连接
        self.semaphore.release()

# 事件
class Worker:
    def __init__(self, event):
        self.event = event
    
    def run(self):
        print("等待信号...")
        self.event.wait()  # 阻塞直到事件被设置
        print("收到信号,开始工作")

def event_demo():
    event = threading.Event()
    workers = [Worker(event) for _ in range(3)]
    
    threads = []
    for worker in workers:
        t = threading.Thread(target=worker.run)
        t.start()
        threads.append(t)
    
    time.sleep(2)
    print("发送开始信号")
    event.set()  # 唤醒所有等待的线程

2. 进程锁

python

复制代码
from multiprocessing import Lock, Value, Process

def increment_counter(lock, counter):
    for _ in range(1000):
        with lock:
            counter.value += 1

def process_lock_demo():
    counter = Value('i', 0)
    lock = Lock()
    
    processes = []
    for _ in range(4):
        p = Process(target=increment_counter, args=(lock, counter))
        p.start()
        processes.append(p)
    
    for p in processes:
        p.join()
    
    print(f"最终计数: {counter.value}")

五、异步与同步对比

同步阻塞模式:

python

复制代码
import socket

def sync_client():
    # 同步阻塞式客户端
    sock = socket.socket()
    sock.connect(('example.com', 80))  # 阻塞直到连接建立
    sock.send(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
    data = sock.recv(1024)  # 阻塞直到数据到达
    print(data.decode())
    sock.close()

异步非阻塞模式:

python

复制代码
import asyncio

async def async_client():
    # 异步非阻塞式客户端
    reader, writer = await asyncio.open_connection('example.com', 80)
    writer.write(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
    await writer.drain()
    data = await reader.read(1024)
    print(data.decode())
    writer.close()

六、选择指南

如何选择并发模型:

场景 推荐方案 原因
CPU密集型计算 多进程 绕过GIL,真正并行
I/O密集型,高并发 协程 轻量级,高并发
简单并行任务 线程池 易于使用
GUI应用 多线程 保持UI响应
数据处理流水线 多进程+队列 充分利用多核
Web服务器 异步+协程 高并发连接

性能对比示例:

python

复制代码
import concurrent.futures
import asyncio
import aiohttp
import requests

def thread_download(urls):
    """线程池下载"""
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        futures = [executor.submit(requests.get, url) for url in urls]
        results = [f.result() for f in futures]
    return results

async def async_download(urls):
    """异步下载"""
    async with aiohttp.ClientSession() as session:
        tasks = [session.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)
        results = [await r.text() for r in responses]
    return results

# 根据场景选择:
# - 少量大文件:多线程
# - 大量小文件:协程
# - 复杂计算:多进程

七、最佳实践

  1. 避免共享状态:尽量使用消息传递而非共享内存

  2. 使用线程/进程池:避免频繁创建销毁

  3. 正确处理异常:防止静默失败

  4. 设置超时:避免死锁

  5. 资源清理:确保释放锁、连接等资源

python

复制代码
# 使用上下文管理器确保资源释放
def safe_transfer(account1, account2, amount):
    # 按固定顺序获取锁,避免死锁
    lock1, lock2 = sorted([account1.lock, account2.lock], key=id)
    
    with lock1:
        with lock2:
            account1.withdraw(amount)
            account2.deposit(amount)

# 使用 asyncio 的超时机制
async def fetch_with_timeout(url, timeout=5):
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url, timeout=timeout) as response:
                return await response.text()
    except asyncio.TimeoutError:
        print(f"请求超时: {url}")
        return None

总结

  1. 多线程:适合I/O密集型,简单并行任务

  2. 多进程:适合CPU密集型,需要内存隔离的任务

  3. 协程:适合高并发I/O,网络应用

  4. :保护共享资源,注意避免死锁

  5. 同步/异步:根据业务需求选择阻塞或非阻塞模型

选择正确的并发模型需要综合考虑任务类型、资源限制、开发复杂度等因素。在实际应用中,常常组合使用多种技术以达到最佳效果。

相关推荐
Hgfdsaqwr2 小时前
内存泄漏检测与防范
开发语言·c++·算法
m0_706653232 小时前
Python生成器(Generator)与Yield关键字:惰性求值之美
jvm·数据库·python
熬夜敲代码的小N2 小时前
Python基础入门:环境配置全指南+核心语法解析
开发语言·python
嫂子开门我是_我哥2 小时前
第十八节:项目实战2:简易通讯录(面向对象+文件持久化实现)
开发语言·python
乙酸氧铍2 小时前
手机使用 ZeroTermux 调用 python 编辑缩放图像
图像处理·python·智能手机·安卓·termux
逄逄不是胖胖2 小时前
《动手学深度学习》-52文本预处理实现
人工智能·pytorch·python·深度学习
MediaTea2 小时前
Python:_sentinel 命名约定
开发语言·python·sentinel
Pyeako2 小时前
opencv计算机视觉--图形透视(投影)变换&图形拼接
人工智能·python·opencv·计算机视觉·图片拼接·投影变换·图形透视变换
茉莉玫瑰花茶2 小时前
C++17 详细特性解析(中)
开发语言·c++