Python并发编程入门:多线程与多进程的10大核心指南

文摘   2024-11-07 09:27   安徽  

Python并发编程入门:多线程与多进程的10大核心指南

【初识并发】

嘿,小伙伴们好!我是资深Python开发工程师老冉。今天要和大家聊一个超级硬核又实用的话题 - 并发编程。

💡 还记得你每次运行耗时任务时,程序像蜗牛一样慢吞吞的感觉吗?或者在处理大量IO操作时,CPU却闲得发慌?这些都是单线程程序的通病。

不用担心,今天我就教你几招,让你的Python程序"分身乘万",秒变并发达人!


【并发进阶之路】

1. 基础概念大拆解 🔸

首先我们要搞清楚几个重要概念:

  • • 进程:就像是一个独立的"小房间",有自己的资源和空间

  • • 线程:则像是房间里的"小精灵",共享房间的资源

📌 当我们启动Python程序时,就创建了一个进程,而在这个进程中,默认运行着一个主线程。

import threading
import multiprocessing

# 查看当前线程
print(f"当前线程名称: {threading.current_thread().name}")
# 查看CPU核心数
print(f"CPU核心数: {multiprocessing.cpu_count()}")

2. 多线程实战 🔸

让我们来看个实际例子,假设我们要同时下载多个文件:

import threading
import time

def download_file(filename):
    print(f"开始下载{filename}")
    time.sleep(2)  # 模拟下载耗时
    print(f"{filename}下载完成")

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

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

3. 进程池的强大威力 🔸

处理CPU密集型任务时,多进程更有优势:

from multiprocessing import Pool

def heavy_calculation(n):
    return sum(i * i for i in range(n))

if __name__ == '__main__':
    numbers = [100000002000000030000000]
    # 创建进程池
    with Pool() as pool:
        results = pool.map(heavy_calculation, numbers)
        print(f"计算结果: {results}")

4. 锁机制保护共享资源 🔸

多个线程同时访问共享资源时,需要加锁保护:

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    with lock:
        current = counter
        time.sleep(0.1)
        counter = current + 1

5. 生产者消费者模式 🔸

from queue import Queue
from threading import Thread

def producer(queue):
    for i in range(5):
        queue.put(f"数据{i}")
        time.sleep(1)

def consumer(queue):
    while True:
        data = queue.get()
        print(f"处理数据: {data}")
        queue.task_done()

# 创建队列和线程
data_queue = Queue()
prod = Thread(target=producer, args=(data_queue,))
cons = Thread(target=consumer, args=(data_queue,))

6. 异步协程的优雅之道 🔸

协程是Python并发编程中的一大利器,特别适合IO密集型任务:

import asyncio

async def fetch_data(id):
    print(f"开始获取数据{id}")
    await asyncio.sleep(1)  # 模拟IO操作
    print(f"数据{id}获取完成")
    return f"数据{id}"

async def main():
    # 创建任务列表
    tasks = [fetch_data(i) for i in range(3)]
    # 并发执行所有任务
    results = await asyncio.gather(*tasks)
    print(f"所有结果: {results}")

# 运行协程
asyncio.run(main())

7. 线程池执行器 🔸

from concurrent.futures import ThreadPoolExecutor
import requests

def fetch_url(url):
    response = requests.get(url)
    return f"{url}{len(response.content)} bytes"

urls = [
    'http://example.com',
    'http://python.org',
    'http://github.com'
]

with ThreadPoolExecutor(max_workers=3as executor:
    results = executor.map(fetch_url, urls)
    for result in results:
        print(result)

8. 信号量控制并发数 🔸

import threading
import time

# 限制最多3个线程同时执行
semaphore = threading.Semaphore(3)

def access_resource(i):
    with semaphore:
        print(f"线程{i}开始访问资源")
        time.sleep(2)
        print(f"线程{i}释放资源")

# 创建10个线程
threads = [
    threading.Thread(target=access_resource, args=(i,))
    for i in range(10)
]

for t in threads:
    t.start()

9. 事件通知机制 🔸

import threading
import time

event = threading.Event()

def wait_for_event():
    print("等待事件发生...")
    event.wait()
    print("收到事件通知!")

def trigger_event():
    time.sleep(3)
    print("触发事件")
    event.set()

# 创建并启动线程
t1 = threading.Thread(target=wait_for_event)
t2 = threading.Thread(target=trigger_event)
t1.start()
t2.start()

10. 条件变量实现同步 🔸

import threading
import time

condition = threading.Condition()
products = []

def producer():
    with condition:
        while len(products) >= 5:
            condition.wait()
        products.append('商品')
        print(f"生产商品,当前库存: {len(products)}")
        condition.notify()

def consumer():
    with condition:
        while len(products) == 0:
            condition.wait()
        products.pop()
        print(f"消费商品,当前库存: {len(products)}")
        condition.notify()

# 创建生产者和消费者线程
producers = [threading.Thread(target=producer) for _ in range(5)]
consumers = [threading.Thread(target=consumer) for _ in range(5)]

for p in producers:
    p.start()
for c in consumers:
    c.start()

【实战启示录】

📌 核心要点总结

  • • CPU密集型任务→多进程

  • • IO密集型任务→多线程或协程

  • • 大量小任务→线程池

  • • 资源受限场景→信号量

  • • 复杂同步需求→条件变量

💡 最佳实践提示

  1. 1. 在选择并发方案时,要根据任务特性选择合适的方式

  2. 2. 共享资源操作时,切记加锁保护

  3. 3. 善用线程池和进程池来管理并发任务

  4. 4. 协程特别适合IO密集型的网络操作


🎉 经过这篇文章的学习,相信你已经掌握了并发编程的核心要点。如果你有任何问题,欢迎在评论区留言交流!让我们一起在Python的海洋里遨游,下期见!

🔍 延伸阅读

  • • Python官方文档的threading模块

  • • Python官方文档的multiprocessing模块

  • • Python官方文档的asyncio模块

Py笔记簿ai
Py笔记簿ai
 最新文章