(7)学习编程---python多进程、多线程、协程

多进程 (Multiprocessing)

概念

多进程是指一个程序同时运行多个进程。每个进程都有自己的内存空间和资源,进程之间通过进程间通信(IPC)来共享数据。

优点

  • 独立性:每个进程都有独立的内存空间,一个进程的崩溃不会影响其他进程。

  • 并行性:可以利用多核 CPU 的优势,实现真正的并行计算。

缺点

  • 资源消耗:每个进程都有自己的内存空间,因此资源消耗较大。

  • 通信成本:进程间通信需要通过 IPC,通信成本较高。

多线程 (Multithreading)

概念

多线程是指一个进程内部同时运行多个线程。线程共享进程的内存空间,因此线程间的通信成本较低。

优点

  • 轻量级:线程比进程轻量,创建和销毁的开销较小。

  • 通信方便:线程共享进程的内存空间,通信成本较低。

缺点

  • GIL 限制:Python 的全局解释器锁(GIL)限制了同一时刻只有一个线程可以执行 Python 字节码,因此多线程在 CPU 密集型任务中无法实现真正的并行计算。Python中的多线程并不是真并行,而是“交替执行” 。由于 GIL 的存在,执行计算密集任务可能会导致多个线程争抢1个GIL,让任务比普通的更慢。

  • 线程安全:线程共享内存空间,因此需要处理线程安全问题,如锁、信号量等。

协程 (Coroutines)

概念

协程是一种基于事件驱动的并发编程模型。协程通过 asyncawait 关键字实现,可以在 I/O 操作时挂起和恢复,从而实现高效的并发。

优点

  • 高效:协程是基于事件驱动的,适合处理 I/O 密集型任务,如网络请求、文件读写等。

  • 轻量级:协程比线程更轻量,创建和切换的开销较小。

缺点

  • 适用场景有限:协程主要用于 I/O 密集型任务,对于 CPU 密集型任务效果不明显。

  • 复杂性:协程的代码逻辑可能较为复杂,需要理解 asyncawait 的使用。

使用 multiprocessing 模块实现多进程

import time
import multiprocessing

# 定义任务函数
def worker(task_id, result_queue):
    print(f"Task {task_id} is starting.")
    time.sleep(1)  # 模拟耗时操作
    result = f"Result from Task {task_id}"
    result_queue.put(result)  # 将结果放入队列
    print(f"Task {task_id} is finished.")

# 主程序
def main():
    num_tasks = 5  # 定义任务数量
    result_queue = multiprocessing.Queue()  # 创建一个队列用于收集结果

    processes = []
    for i in range(num_tasks):
        process = multiprocessing.Process(target=worker, args=(i, result_queue))
        processes.append(process)
        process.start()

    for process in processes:
        process.join()  # 等待所有进程完成

    # 收集结果
    results = []
    while not result_queue.empty():
        results.append(result_queue.get())

    print("All processes have finished.")
    print("Results:", results)

if __name__ == "__main__":
    main()

使用 multiprocessing 和 multiprocessing.dummy分别实现多进程和多线程。

import time
import multiprocessing
import multiprocessing.dummy

# 定义任务函数
def task_function(task_id):
    print(f"Task {task_id} is starting.")
    time.sleep(1)  # 模拟耗时操作
    print(f"Task {task_id} is finished.")

# 使用 multiprocessing 实现多进程
def run_multiprocessing(num_tasks):
    processes = []
    for i in range(num_tasks):
        process = multiprocessing.Process(target=task_function, args=(i,))
        processes.append(process)
        process.start()

    for process in processes:
        process.join()

# 使用 multiprocessing.dummy 实现多线程
def run_multithreading(num_tasks):
    pool = multiprocessing.dummy.Pool(processes=num_tasks)
    pool.map(task_function, range(num_tasks))
    pool.close()
    pool.join()

# 主程序
def main():
    num_tasks = 5  # 定义任务数量

    print("Running multiprocessing...")
    run_multiprocessing(num_tasks)

    print("\nRunning multithreading...")
    run_multithreading(num_tasks)

if __name__ == "__main__":
    main()

使用 multiprocessingthreading 模块实现多进程和多线程结合使用

import time
import threading
import multiprocessing

# 定义任务函数
def task_function(thread_id, process_id):
    print(f"Thread {thread_id} in Process {process_id} is starting.")
    time.sleep(1)  # 模拟耗时操作
    print(f"Thread {thread_id} in Process {process_id} is finished.")

# 定义多线程函数
def process_function(process_id, num_threads):
    threads = []
    for i in range(num_threads):
        thread = threading.Thread(target=task_function, args=(i, process_id))
        threads.append(thread)
        thread.start()
    
    for thread in threads:
        thread.join()

# 主程序
def main():
    num_processes = 3  # 定义进程数量
    num_threads_per_process = 2  # 定义每个进程中的线程数量

    processes = []
    for i in range(num_processes):
        process = multiprocessing.Process(target=process_function, args=(i, num_threads_per_process))
        processes.append(process)
        process.start()

    for process in processes:
        process.join()

    print("All processes and threads have finished.")

if __name__ == "__main__":
    main()

multiprocessing 多进程通信的方法

python 的 multiprocessing模块提供了多种进程间通信的方法,包括队列、管道、共享内存、管理器和锁等。这些方法可以根据具体需求选择使用,以实现复杂的并发任务。

  • 队列(Queue):适用于简单的消息传递。

  • 管道(Pipe):适用于两个进程之间的双向通信。

  • 共享内存(Shared Memory):适用于共享简单的数据类型。

  • 管理器(Manager):适用于共享复杂的数据结构。

  • 锁(Lock):用于同步访问共享资源。

1. 队列(Queue)

multiprocessing.Queue 是一个线程和进程安全的队列实现,用于在进程之间传递消息。

import multiprocessing
import time

def worker(task_id, queue):
    print(f"Task {task_id} is starting.")
    time.sleep(1)  # 模拟耗时操作
    result = f"Result from Task {task_id}"
    queue.put(result)  # 将结果放入队列
    print(f"Task {task_id} is finished.")

def main():
    num_tasks = 5
    result_queue = multiprocessing.Queue()  # 创建一个队列

    processes = []
    for i in range(num_tasks):
        process = multiprocessing.Process(target=worker, args=(i, result_queue))
        processes.append(process)
        process.start()

    for process in processes:
        process.join()  # 等待所有进程完成

    # 收集结果
    results = []
    while not result_queue.empty():
        results.append(result_queue.get())

    print("All processes have finished.")
    print("Results:", results)

if __name__ == "__main__":
    main()

2. 管道(Pipe)

multiprocessing.Pipe 提供了一个双向通信通道,可以用于两个进程之间的通信。

import multiprocessing
import time

def worker(conn):
    print("Worker is starting.")
    time.sleep(1)  # 模拟耗时操作
    conn.send("Hello from worker!")  # 发送消息
    conn.close()
    print("Worker is finished.")

def main():
    parent_conn, child_conn = multiprocessing.Pipe()  # 创建管道

    process = multiprocessing.Process(target=worker, args=(child_conn,))
    process.start()

    print("Main process is waiting for message.")
    message = parent_conn.recv()  # 接收消息
    print("Received message:", message)

    process.join()
    print("Main process is finished.")

if __name__ == "__main__":
    main()

3. 共享内存(Shared Memory)

multiprocessing 提供了 ValueArray,用于在进程之间共享数据。

 

import multiprocessing
import time

def worker(shared_value, shared_array):
    print("Worker is starting.")
    shared_value.value = 42  # 修改共享值
    for i in range(len(shared_array)):
        shared_array[i] = i * i  # 修改共享数组
    time.sleep(1)
    print("Worker is finished.")

def main():
    shared_value = multiprocessing.Value('i', 0)  # 创建共享值
    shared_array = multiprocessing.Array('i', 5)  # 创建共享数组

    process = multiprocessing.Process(target=worker, args=(shared_value, shared_array))
    process.start()
    process.join()

    print("Shared value:", shared_value.value)
    print("Shared array:", list(shared_array))

if __name__ == "__main__":
    main()

 4. 管理器(Manager)

multiprocessing.Manager 提供了一种高级的共享数据机制,可以创建一个管理器对象,用于在多个进程之间共享复杂的数据结构。

import multiprocessing
import time

def worker(dictionary, list_):
    dictionary[1] = '1'
    dictionary['2'] = 2
    dictionary[0.25] = None
    list_.reverse()

def main():
    with multiprocessing.Manager() as manager:
        shared_dict = manager.dict()  # 创建共享字典
        shared_list = manager.list(range(10))  # 创建共享列表

        process = multiprocessing.Process(target=worker, args=(shared_dict, shared_list))
        process.start()
        process.join()

        print("Shared dictionary:", shared_dict)
        print("Shared list:", shared_list)

if __name__ == "__main__":
    main()

5. 锁(Lock)

multiprocessing.Lock 提供了一种同步机制,用于在多个进程之间同步访问共享资源。

import multiprocessing
import time

def worker(lock, counter):
    with lock:  # 使用上下文管理器获取锁
        print(f"Worker {counter} is starting.")
        time.sleep(1)
        print(f"Worker {counter} is finished.")

def main():
    lock = multiprocessing.Lock()  # 创建锁
    processes = []

    for i in range(5):
        process = multiprocessing.Process(target=worker, args=(lock, i))
        processes.append(process)
        process.start()

    for process in processes:
        process.join()

    print("All processes have finished.")

if __name__ == "__main__":
    main()

python multiprocessing.dummy 多线程通信的方法

  1. 共享变量:直接使用全局变量进行通信。

  2. 锁(Lock):使用 threading.Lock 确保线程安全。

  3. 条件变量(Condition):使用 threading.Condition 实现线程间的同步和通知。

  4. 队列(Queue):使用 queue.Queue 实现线程间的消息传递。

  5. 事件(Event):使用 threading.Event 实现线程间的同步信号。

1. 共享变量

multiprocessing.dummy 中,线程共享内存空间,因此可以直接使用共享变量进行通信。

from multiprocessing.dummy import Pool as ThreadPool
import time

# 共享变量
shared_data = []

def worker(task_id):
    global shared_data
    shared_data.append(f"Result from Task {task_id}")
    time.sleep(1)  # 模拟耗时操作
    print(f"Task {task_id} finished.")

def main():
    pool = ThreadPool(3)  # 创建线程池
    pool.map(worker, range(5))  # 执行任务
    pool.close()
    pool.join()

    print("All tasks finished.")
    print("Shared data:", shared_data)

if __name__ == "__main__":
    main()

2. 锁(Lock)

虽然 multiprocessing.dummy 没有直接提供锁,但可以使用 threading.Lock 来同步线程。

from multiprocessing.dummy import Pool as ThreadPool
import threading
import time

# 共享变量和锁
shared_data = []
lock = threading.Lock()

def worker(task_id):
    with lock:  # 使用锁确保线程安全
        shared_data.append(f"Result from Task {task_id}")
        print(f"Task {task_id} finished.")

def main():
    pool = ThreadPool(3)  # 创建线程池
    pool.map(worker, range(5))  # 执行任务
    pool.close()
    pool.join()

    print("All tasks finished.")
    print("Shared data:", shared_data)

if __name__ == "__main__":
    main()

3. 条件变量(Condition)

multiprocessing.dummy 没有直接提供条件变量,但可以使用 threading.Condition

import threading
from multiprocessing.dummy import Pool as ThreadPool
import time

condition = threading.Condition()
shared_resource = 0

def producer():
    global shared_resource
    with condition:
        shared_resource += 1
        print(f"Produced {shared_resource}")
        condition.notify()  # 通知等待的线程

def consumer():
    global shared_resource
    with condition:
        condition.wait()  # 等待资源可用
        print(f"Consumed {shared_resource}")

def main():
    producer_thread = threading.Thread(target=producer)
    consumer_thread = threading.Thread(target=consumer)

    producer_thread.start()
    consumer_thread.start()

    producer_thread.join()
    consumer_thread.join()

    print("All tasks finished.")

if __name__ == "__main__":
    main()

4. 队列(Queue)

multiprocessing.dummy 没有直接提供队列,但可以使用 queue.Queue

import queue
from multiprocessing.dummy import Pool as ThreadPool
import time

q = queue.Queue()

def producer():
    for i in range(5):
        time.sleep(1)
        q.put(i)
        print(f"Produced {i}")

def consumer():
    while True:
        item = q.get()
        print(f"Consumed {item}")
        q.task_done()

def main():
    producer_thread = threading.Thread(target=producer)
    consumer_thread = threading.Thread(target=consumer)

    producer_thread.start()
    consumer_thread.start()

    producer_thread.join()
    q.join()  # 等待队列为空

    print("All tasks finished.")

if __name__ == "__main__":
    main()

 

5. 事件(Event)

multiprocessing.dummy 没有直接提供事件,但可以使用 threading.Event

import threading
from multiprocessing.dummy import Pool as ThreadPool
import time

event = threading.Event()

def worker():
    print("Worker: Waiting for event...")
    event.wait()  # 等待事件被设置
    print("Worker: Event received.")

def main():
    worker_thread = threading.Thread(target=worker)
    worker_thread.start()

    time.sleep(2)  # 模拟耗时操作
    event.set()  # 设置事件

    worker_thread.join()
    print("All tasks finished.")

if __name__ == "__main__":
    main()

 

python threading 多线程通信

Python 中多线程通信的几种常见方法,

  1. 共享变量:通过锁(Lock)确保线程安全。

  2. 锁(Lock):用于同步线程对共享资源的访问。

  3. 条件变量(Condition):用于线程间的同步和通知。

  4. 队列(Queue):线程安全的队列,用于线程间的消息传递。

  5. 事件(Event):用于线程间的同步信号。

1. 共享变量

import threading

# 共享变量
shared_counter = 0
lock = threading.Lock()

def increment():
    global shared_counter
    with lock:  # 确保线程安全
        shared_counter += 1
        print(f"Counter incremented to {shared_counter}")

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

    for thread in threads:
        thread.join()

    print(f"Final counter value: {shared_counter}")

if __name__ == "__main__":
    main()


# 共享变量的用途:
# 共享变量 shared_counter 用于简单的计数。
# 每个线程将计数器加 1,并打印当前值。
# 目的是展示如何通过锁确保线程安全地更新共享变量。

# 锁的使用:
# 使用 lock 确保 shared_counter 的更新是线程安全的。
# 锁的范围是 shared_counter += 1 操作,确保每次只有一个线程可以修改计数器。

2. 锁(Lock)

import threading

lock = threading.Lock()
shared_resource = 0

def worker():
    global shared_resource
    with lock:  # 使用上下文管理器获取锁
        temp = shared_resource
        temp += 1
        time.sleep(0.1)  # 模拟耗时操作
        shared_resource = temp
        print(f"Resource updated to {shared_resource}")

def main():
    threads = []
    for _ in range(5):
        thread = threading.Thread(target=worker)
        threads.append(thread)
        thread.start()

    for thread in threads:
        thread.join()

    print(f"Final shared resource value: {shared_resource}")

if __name__ == "__main__":
    main()

# 共享变量的用途
# 共享变量 shared_resource 用于模拟资源更新。
# 每个线程读取当前值,将其加 1,然后写回共享变量。
# 目的是展示如何在有耗时操作(如 time.sleep(0.1))的情况下,通过锁确保线程安全地更新共享资源。

# 锁的使用
# 使用 lock 确保 shared_resource 的更新是线程安全的。
# 锁的范围包括读取、修改和写回共享资源的整个过程,确保在有耗时操作时,其他线程不会干扰当前线程的操作。

3. 条件变量(Condition)

import threading
import time

condition = threading.Condition()
item_produced = False

def producer():
    global item_produced
    with condition:
        print("Producer: Producing item...")
        item_produced = True
        condition.notify()  # 通知等待的线程
        print("Producer: Item produced and notified.")

def consumer():
    global item_produced
    with condition:
        print("Consumer: Waiting for item...")
        condition.wait_for(lambda: item_produced)  # 等待条件满足
        print("Consumer: Item consumed.")
        item_produced = False

def main():
    producer_thread = threading.Thread(target=producer)
    consumer_thread = threading.Thread(target=consumer)

    producer_thread.start()
    consumer_thread.start()

    producer_thread.join()
    consumer_thread.join()

if __name__ == "__main__":
    main()

4. 队列(Queue)

import threading
import queue
import time

def producer(q):
    for i in range(5):
        print(f"Producer: Producing item {i}")
        q.put(i)
        time.sleep(0.5)

def consumer(q):
    while True:
        item = q.get()
        print(f"Consumer: Consumed item {item}")
        q.task_done()

def main():
    q = queue.Queue()
    producer_thread = threading.Thread(target=producer, args=(q,))
    consumer_thread = threading.Thread(target=consumer, args=(q,))

    producer_thread.start()
    consumer_thread.start()

    producer_thread.join()
    q.join()  # 等待队列为空
    consumer_thread.join()

if __name__ == "__main__":
    main()

5. 事件(Event)

import threading
import time

event = threading.Event()

def waiter():
    print("Waiter: Waiting for event...")
    event.wait()  # 等待事件被设置
    print("Waiter: Event received.")

def setter():
    time.sleep(2)  # 模拟耗时操作
    print("Setter: Setting event.")
    event.set()  # 设置事件

def main():
    waiter_thread = threading.Thread(target=waiter)
    setter_thread = threading.Thread(target=setter)

    waiter_thread.start()
    setter_thread.start()

    waiter_thread.join()
    setter_thread.join()

if __name__ == "__main__":
    main()

python 实现协程

import asyncio

# 使用 async def 定义协程函数
async def fetch_data(task_id):
    print(f"Fetching data for task {task_id}...")
    await asyncio.sleep(2)  # 模拟异步 I/O 操作
    print(f"Data fetched for task {task_id}.")
    return f"Sample data for task {task_id}"

# 使用 asyncio.run() 运行协程
async def main():
    data = await fetch_data(1)
    print("Received data:", data)

# 使用 asyncio.gather() 并发运行多个协程
async def main2():
    tasks = [fetch_data(i) for i in range(3)]
    results = await asyncio.gather(*tasks)
    print("Received data:", results)

if __name__ == "__main__":
    asyncio.run(main())
    asyncio.run(main2())

协程的常见应用场景

 网络请求

协程非常适合处理网络请求,如使用 aiohttp 库进行异步 HTTP 请求。

import asyncio
import aiohttp

async def fetch_url(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            data = await response.text()
            print(f"Fetched {url}: {data[:100]}...")

async def main():
    urls = [
        "http://example.com",
        "http://example.org",
        "http://example.net"
    ]
    tasks = [fetch_url(url) for url in urls]
    await asyncio.gather(*tasks)

if __name__ == "__main__":
    asyncio.run(main())

文件读写

协程也可以用于文件读写操作,如使用 aiofiles 库进行异步文件操作。

import asyncio
import aiofiles

async def read_file(file_path):
    async with aiofiles.open(file_path, 'r') as file:
        content = await file.read()
        print(f"Read from {file_path}: {content[:100]}...")

async def main():
    files = ["file1.txt", "file2.txt", "file3.txt"]
    tasks = [read_file(file) for file in files]
    await asyncio.gather(*tasks)

if __name__ == "__main__":
    asyncio.run(main())

 

协程的高级特性

错误处理

协程中的错误可以通过 try-except 块捕获。

import asyncio

async def fetch_data(task_id):
    try:
        print(f"Task {task_id}: Fetching data...")
        await asyncio.sleep(2)
        print(f"Task {task_id}: Data fetched.")
        return f"Sample data {task_id}"
    except Exception as e:
        print(f"Task {task_id}: Error occurred: {e}")

async def main():
    tasks = [fetch_data(i) for i in range(3)]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    print("Received data:", results)

if __name__ == "__main__":
    asyncio.run(main())

超时处理

协程支持超时处理,可以使用 asyncio.wait_for()

import asyncio

async def fetch_data():
    print("Fetching data...")
    await asyncio.sleep(5)  # 模拟耗时操作
    print("Data fetched.")
    return "Sample data"

async def main():
    try:
        data = await asyncio.wait_for(fetch_data(), timeout=3)
        print("Received data:", data)
    except asyncio.TimeoutError:
        print("Timeout occurred.")

if __name__ == "__main__":
    asyncio.run(main())

协程的限制

仅适用于 I/O 密集型任务

协程主要适用于 I/O 密集型任务,对于 CPU 密集型任务效果不明显。

事件循环的限制

协程运行在单个线程中,依赖于事件循环。如果事件循环被阻塞,协程将无法运行。

协程之间的通信

  • asyncio.Queue:适用于生产者和消费者模式,支持异步操作。

  • asyncio.Event:用于简单的同步场景,一个协程可以设置事件,另一个协程可以等待事件。

  • asyncio.Condition:提供更复杂的同步机制,适用于需要条件变量的场景。

  • asyncio.create_task:用于并发运行多个协程,协程间可以通过共享变量或其他机制进行通信

1. 使用 asyncio.Queue 实现协程间通信

asyncio.Queue 是一个异步队列,支持 async/await 语法,适用于协程间的通信。

import asyncio

async def producer(queue):
    for i in range(5):
        print(f"Producing item {i}")
        await queue.put(i)  # 将数据放入队列
        await asyncio.sleep(1)

async def consumer(queue):
    while True:
        item = await queue.get()  # 从队列中获取数据
        print(f"Consuming item {item}")
        queue.task_done()  # 标记任务完成

async def main():
    queue = asyncio.Queue()  # 创建异步队列
    producer_task = asyncio.create_task(producer(queue))  # 创建生产者任务
    consumer_task = asyncio.create_task(consumer(queue))  # 创建消费者任务

    await producer_task  # 等待生产者任务完成
    await queue.join()  # 等待队列中的所有任务完成
    consumer_task.cancel()  # 取消消费者任务

asyncio.run(main())

2. 使用 asyncio.Event 实现协程间通信

asyncio.Event 可用于协程间的同步,一个协程可以设置事件,另一个协程可以等待事件。

import asyncio

async def waiter(event):
    print("Waiter: Waiting for event...")
    await event.wait()  # 等待事件
    print("Waiter: Event received.")

async def setter(event):
    print("Setter: Setting event...")
    await asyncio.sleep(2)  # 模拟耗时操作
    event.set()  # 设置事件

async def main():
    event = asyncio.Event()
    waiter_task = asyncio.create_task(waiter(event))
    setter_task = asyncio.create_task(setter(event))

    await waiter_task  # 等待等待者任务完成
    await setter_task  # 等待设置者任务完成

asyncio.run(main())

3. 使用 asyncio.Condition 实现协程间通信

asyncio.Condition 提供了更复杂的同步机制,允许协程在满足特定条件时进行通信。

import asyncio

async def producer(condition, shared_list):
    async with condition:
        shared_list.append("Item")
        print("Producer: Item produced.")
        condition.notify()  # 通知等待的协程

async def consumer(condition, shared_list):
    async with condition:
        await condition.wait()  # 等待条件变量
        item = shared_list.pop()
        print(f"Consumer: Item consumed: {item}")

async def main():
    shared_list = []
    condition = asyncio.Condition()
    producer_task = asyncio.create_task(producer(condition, shared_list))
    consumer_task = asyncio.create_task(consumer(condition, shared_list))

    await producer_task
    await consumer_task

asyncio.run(main())

4. 使用 asyncio.create_task 实现协程间通信

asyncio.create_task 可用于并发运行多个协程,这些协程可以通过共享变量或其他机制进行通信。

import asyncio

async def task1(shared_data):
    await asyncio.sleep(1)
    shared_data.append("Task 1 result")
    print("Task 1 completed.")

async def task2(shared_data):
    await asyncio.sleep(2)
    shared_data.append("Task 2 result")
    print("Task 2 completed.")

async def main():
    shared_data = []
    task1_coro = asyncio.create_task(task1(shared_data))  # 创建任务1
    task2_coro = asyncio.create_task(task2(shared_data))  # 创建任务2

    await task1_coro  # 等待任务1完成
    await task2_coro  # 等待任务2完成

    print("Shared data:", shared_data)

asyncio.run(main())

你可能感兴趣的:(编程,学习)