python异步编程、多线程、多进程原理 您所在的位置:网站首页 线程同步和异步的方法 python异步编程、多线程、多进程原理

python异步编程、多线程、多进程原理

2024-06-02 14:00| 来源: 网络整理| 查看: 265

协程与线程、进程的区别

协程(Coroutine):

协程是一种比线程更加轻量级的存在。它依赖于语言的支持(或者库的支持),允许开发者在单个线程中执行多个“任务”。 协程依赖于异步编程模型,能够挂起和恢复执行点,通常用于I/O密集型任务。 协程的调度完全由程序控制,而非操作系统。 线程(Thread):

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。 线程共享进程的堆空间,但每个线程有自己的栈空间。 线程的调度由操作系统控制,可以实现并发执行。 进程(Process):

进程是系统进行资源分配和调度的一个独立单位。 每个进程都有自己独立的代码和数据空间(内存),进程间通信(IPC)需要特定的机制。

异步编程(协程)

python异步编程主要是通过协程实现的。

协程既不是线程,也不是进程。它是一种用户态的轻量级线程,由程序控制其执行,而非由操作系统调度。协程拥有自己的寄存器上下文和栈,但它们共享同一进程中的堆空间,这使得协程切换的开销远小于传统的线程切换。

本质:允许程序在等待某些操作完成时能够继续执行,而不是被阻塞等待。适合IO密集型任务,比如网络请求,文件读写等,这些操作大部分时间都在等待外部资源,而CPU的计算能力没有得到充分利用。异步编程的核心原则是最大限度减少阻塞

原理: Coroutines协程:异步编程依赖于协程。通过async def定义函数。协程允许在等待操作时,暂停执行让出控制权,给Event Loop处理其他任务。 事件循环(Event Loop):事件循环是异步编程的核心,负责管理和调度执行所有的协程。当协程被暂停时,Event Loop找出可以执行的其他协程并继续进行,知道原始协程可以再次进行。

新概念: async def: 用于定义一个协程函数。 async with用于创建异步上下文管理器,通常用于管理资源的获取和释放。 await: 用于“等待”一个可等待对象(如另一个协程)完成,期间协程会被挂起,控制权返回给事件循环。 事件循环: 控制协程的调度执行。

两个包: asyncio:用于异步编程。 aiohttp:用于异步HTTP请求。

代码:

import asyncio import aiohttp async def download_page(session, url): async with session.get(url) as response:#使用异步上下文管理器 async with 发起GET请求,并等待响应 return await response.text()#返回响应的文本内容。 async def process_pages(urls): async with aiohttp.ClientSession() as session:#建一个异步HTTP会话对象 session # semaphore = asyncio.Semaphore(10) # 限制并发请求数量为10 tasks = [download_page(session, url) for url in urls] pages = await asyncio.gather(*tasks)#等待所有任务完成,获取所有页面的内容并存储在 pages 中。 for page in pages: # 处理每个页面的内容 print(len(page)) urls = ["http://example.com", "https://api.github.com", "https://www.python.org"] asyncio.run(process_pages(urls)) #asyncio.run()用于运行异步函数作为主程序的入口点,启动事件循环并运行异步任务。 多线程

多线程是通过threading模块实现,允许程序同时运行多个线程。多进程特别适合于CPU密集型任务。

本质:多线程的本质是允许程序在单个进程内并行执行多个任务。在多线程环境中,当两个或更多的线程尝试同时修改某个共享数据时,可能会导致数据不一致的问题。因此,确保对共享资源的访问是同步的,是多线程编程的第一原则。

原理:

GIL(Global Interpreter Lock) :python(CPython)有一个全局解释器锁的机制,确保任何时刻只有一个线程在执行python字节码。这意味着即使在多核处理器上,python多线程也无法真正实现并行。并发而非并行:python多线程更适合执行I/O密集型任务(文件操作,网络请求),而非CPU密集任务(科学计算、图像处理、3D渲染)

新概念:

Thread类: threading.Thread是代表一个线程的类。锁(Lock)和信号量(Semaphore): 用于线程同步。条件变量(Condition): 用于复杂的线程间同步。

使用锁实现线程同步

import threading x = 0 lock = threading.Lock() def increment(): global x for _ in range(100000): lock.acquire() x += 1 lock.release() # 创建两个线程 t1 = threading.Thread(target=increment) t2 = threading.Thread(target=increment) t1.start() t2.start() t1.join() t2.join() print(x) import os import threading def test(file_path): print(file_path) thread_list = [] files_list = os.listdir('xx') for file_path in files_list: # 12个任务 t = threading.Thread(target=test, args= [file_path]) thread_list.append(t) for t in thread_list: # pool_sema.acquire() t.start() # 调用start()方法,开始执行 for t in thread_list: t.join() # 子线程调用join()方法,使主线程等待子线程运行完毕之后才退出

线程池管理线程

import concurrent.futures def square(num): return num * num if __name__ == '__main__': with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: # 使用线程池的map方法,同样按照参数顺序返回计算结果 numbers = [1, 2, 3, 4, 5] results = list(executor.map(square, numbers)) print("Squared numbers (using threads):", results) 多进程

靠multiprocessing模块来实现。多进程允许同时运行多个程序实例,每个实例运行在自己独立的进程中。

本质:多进程的本质是利用计算机多核处理器的能力,通过创建多个独立的进程来实现程序的并行执行。尽管每个进程有自己独立的执行环境,但在需要时它们之间应该能够有效地通信。multiprocessing模块提供了多种通信机制,包括管道和队列。

原理 Python的multiprocessing模块提供了一个与threading模块相似的API,但它使用子进程而非线程。 每个子进程都有自己独立的内存空间和Python解释器实例,这避免了GIL(全局解释器锁)限制。

代码: 生产者消费者模型

from multiprocessing import Process, Queue def producer(queue): items = ["item1", "item2", "item3"] # 多个元素 for item in items: queue.put(item) def consumer(queue): while not queue.empty(): item = queue.get() print(f"Consumed {item}") if __name__ == "__main__": queue = Queue() p1 = Process(target=producer, args=(queue,)) p2 = Process(target=consumer, args=(queue,)) p1.start() p2.start() p1.join() p2.join()

使用multiprocessing.Pool来将大量图片转换为灰度图:

from multiprocessing import Pool import cv2 import os def convert_to_grayscale(image_path): # 读取图片 img = cv2.imread(image_path) # 转换为灰度图 gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 保存灰度图,这里假设你想在同一目录下保存,可以根据需要调整保存路径 gray_img_path = os.path.splitext(image_path)[0] + '_gray' + os.path.splitext(image_path)[1] cv2.imwrite(gray_img_path, gray_img) def main(image_paths): # 创建进程池 with Pool(processes=os.cpu_count()) as pool: # 使用map同步并行处理所有图片 pool.map(convert_to_grayscale, image_paths) if __name__ == "__main__": # 假设有一个包含所有图片路径的列表 image_paths = ["path/to/image1.jpg", "path/to/image2.jpg", "..."] main(image_paths)

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有