? ? ? ? 导读:Python提供了多种机制来支持并发编程,这些机制包括线程、进程、协程以及异步编程模型,这篇文章让我们一一讨论。
目录
multiprocessing
模块创建进程import multiprocessing
import os
def square_numbers():
for i in range(5):
print(f" {i} 的平方 {i * i}")
print(f"进程 ID: {os.getpid()}")
if __name__ == '__main__':
# 创建进程
process = multiprocessing.Process(target=square_numbers)
# 启动进程
process.start()
# 等待进程完成
process.join()
print("进程结束")
可以看到,整个任务期间,我们一直是使用创建的进程来执行任务。
threading
模块创建线程创建线程的方法类似:
import threading
import time
def print_numbers():
for i in range(1, 6):
time.sleep(1)
print(f"======》 {i}")
# 创建线程
thread = threading.Thread(target=print_numbers)
# 启动线程
thread.start()
# 等待线程完成
thread.join()
print("=======结束")
至此为止,我们已经可以创建单独的进程和线程,并使用它来执行一些我们封装好的函数。但目前看来使用线程或者进程好像没啥用是吧,别急,你现在可以把他当做一个知识点,一个没什么用但是你掌握了的知识点,后面会有大用,在继续之前 我们来补充一点注意事项:
async def
关键字定义的。await
关键字暂停协程的执行,直到等待的操作完成。asyncio
库是实现异步编程的关键。话不多说,上代码:
asyncio
进行异步import asyncio
async def count():
print("=====1======")
await asyncio.sleep(1)
print("======2=======")
async def main():
await asyncio.gather(count(), count(), count())
asyncio.run(main())
输出结果:
分析:使用
asyncio
库来运行三个异步协程。在打印完第一次的结果后,睡了一秒,但因为同时有三个协程在工作,所以输出了三个,一秒后,又是三个2打印了出来。
异步爬虫必备:aiohttp
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://www.baidu.com')
print(html)
asyncio.run(main())
咳咳,爬虫虽好,也不要给对方太大压力哦
好了 分析一下它的优势:
非阻塞性,异步编程允许程序在等待操作完成时继续执行,提高了程序的响应性和效率。
资源利用,对于I/O密集型任务,异步编程可以帮助更好地利用系统资源,避免因等待I/O操作而闲置。
简化复杂性,虽然异步编程在一开始可能看起来复杂,但它可以帮助简化某些类型的并发代码。
注意
async
和await
关键字是关键。当多个线程或进程尝试同时访问和修改共享数据时,可能会导致数据不一致和不可预测。
- 使用互斥锁(Mutex)或其他同步机制(如信号量、临界区)来确保在任一时刻只有一个线程可以访问共享资源。
- 在Python中,可以使用
threading.Lock
来实现线程安全的操作。
import threading
class Counter:
def __init__(self):
self.value = 0
self.lock = threading.Lock()
def increment(self):
with self.lock:
self.value += 1
- 死锁是指两个或多个线程相互等待对方释放资源,从而导致它们都停止执行的情况。
- 死锁通常发生在多个线程需要相同的锁,但以不同的顺序获取它们时。
- 避免多个锁的不必要使用。
- 保证所有线程以相同的顺序获取锁。
- 使用超时机制强制释放锁,或检测并打破死锁。
- 活锁是指线程虽然没有被阻塞(仍在运行),但无法继续执行有用的工作。
- 资源饥饿发生在某些线程无法获取必要的资源,而其他线程则持续运行。
- 重新设计算法和工作流,以确保所有线程都有机会执行。
- 实现合理的资源分配策略和优先级制度。
- 在并发环境中,操作系统经常需要在不同的线程或进程之间进行上下文切换。
- 这些切换可能导致显著的性能开销。
- 优化线程数量,避免创建过多的线程。
- 使用线程池来管理线程,减少创建和销毁线程的次数。
所有案例不提供业务逻辑部分,只是讨论并发操作。
- 网络服务器常常需要同时处理成千上万的客户端请求。
- 使用多线程或异步编程可以有效地提高服务器的响应能力和吞吐量。
import asyncio
from aiohttp import web
async def handle(request):
return web.Response(text="Hello, world")
app = web.Application()
app.router.add_get('/', handle)
web.run_app(app)
- 在数据分析和数据科学中,经常需要处理大量数据集。
- 并发编程(特别是多进程处理)可以在这些任务中大幅提高处理速度。
from multiprocessing import Pool
def process_data(data):
# 数据处理逻辑
return result
if __name__ == '__main__':
pool = Pool(processes=4)
data_to_process = [data1, data2, data3, data4]
results = pool.map(process_data, data_to_process)
- Web爬虫需要从多个网页上快速有效地收集数据。
- 使用多线程或异步编程可以同时发送多个网络请求,显著提高数据收集的效率。
(感谢 baidu 提供的友情支持? -狗头) 一般情况不建议大家高并发爬取一个网站,会给对方网站造成很大压力,不要让网站成为网战。
import asyncio
import aiohttp
async def fetch_page(url, session):
async with session.get(url) as response:
return await response.text()
async def main(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch_page(url, session) for url in urls]
return await asyncio.gather(*tasks)
urls = ["http://www.baidu.com/a", "http://www.baidu.com/b", "http://www.baidu.com/c"]
pages = asyncio.run(main(urls))
-------------
觉得不错 点个赞吧~