当前位置: 首页 > news >正文

Python----Python高级(并发编程:协程Coroutines,事件循环,Task对象,协程间通信,协程同步,将协程分布到线程池/进程池中)

一、协程

1.1、协程

协程,Coroutines,也叫作纤程(Fiber)

协程,全称是“协同程序”,用来实现任务协作。是一种在线程中,比线程更加轻量级的存在,由程序员自己写程序来管理。

当出现IO阻塞时,CPU一直等待IO返回,处于空转状态。这时候用协程,可以执行其他任务。当IO返回结果后,再回来处理数据。充分利用了IO等待的时间,提高了效率。

进程与线程是操作系统管理和调度的基本单位,而协程则是由程序员 实现的一种轻量级的、用户空间级别的多任务机制,通常不由操作系统直接提供支持。

1.2、协程的核心(控制流的让出和恢复)

1.每个协程有自己的执行栈,可以保存自己的执行现场

2.可以由用户程序按需创建协程(比如:遇到io操作)

3.协程“主动让出(yield)”执行权时候,会保存执行现场(保存中断时的寄存器上下文和栈),然后切换到其他协程

4.协程恢复执行(resume)时,根据之前保存的执行现场恢复到中断前的状态,继续执行,这样就通过协程实现了轻量的由用户态调度的多任务模型

1.3、协程的特点

1. 占用资源少:协程通常只需要少量的栈空间,这是因为它们采用协作式的多任务 处理机制,可以在固定的栈空间内通过状态保存和恢复来实现任务的切换,相比 多线程和多进程,协程占用的系统资源更少。

2. 切换开销小:协程的切换是在用户态进行的,不需要进行系统调用,也不涉及内 核态的上下文切换,因此其切换开销非常小,远远低于线程间的上下文切换。

3. 可暂停和可恢复的函数:协程允许函数在执行过程中主动暂停(通常是遇到I/O操 作或其他耗时操作时),并将控制权交还给调度器,以便其他协程可以运行。在 I/O操作或其他耗时操作完成后,该协程可以从暂停的地方继续执行,而不会阻塞 整个线程。这种特性使得协程非常适合于处理I/O密集型任务,可以在等待I/O操 作完成时释放CPU,从而提高程序的并发性能和资源利用率。

1.4、协程的优点

1.由于自身带有上下文和栈,无需线程上下文切换的开销,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级;

2.无需原子操作的锁定及同步的开销;

3.方便切换控制流,简化编程模型

4.单线程内就可以实现并发的效果,最大限度地利用cpu,且可扩展性高,成本低(注:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理)

asyncio协程是写爬虫比较好的方式。比多线程和多进程都好. 开辟新的线程和进程是非常耗时的。

1.5、协程的缺点

1.无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上。

1.6、与进程和线程的比较

多进程是在操作系统层面实现的并行执行方式,每个进程拥有独立的内存空间和 系统资源,进程间通过进程间通信(IPC)机制(如管道、消息队列、共享内存 等)进行交互,这增加了通信的复杂性。多进程可以充分利用多核处理器的性 能,实现真正的并行计算。由于进程间的隔离性,系统的安全性和稳定性也得到 了提高。然而,进程间通信和同步的开销相对较高,且每个进程的创建和销毁通 常伴随着较大的资源开销。

多线程是在一个进程内部实现的并发执行方式,多个线程共享该进程的内存空间 和资源,这使得线程间通信和数据共享相对容易。但是,这也引入了线程安全问题,需要通过同步机制(如互斥锁、信号量、条件变量等)来避免数据冲突。多 线程的优点在于能够实现并发执行,但线程间的上下文切换开销相比进程较小, 但相比协程则较大,且需要谨慎处理线程安全问题。 

协程是一种轻量级的线程,与多进程和多线程相比,它具有占用资源少、切换开 销小、可以实现高效异步执行等优点。协程通过非阻塞I/O操作来等待数据,当数 据就绪时自动恢复执行,从而提高了程序的执行效率和响应速度。然而,协程也 有其局限性,它只能在单个线程内执行,因此它对于CPU密集型任务来说并没有 什么好处。 

二、实现协程的方法 

在python中,实现协程的方法有以下几种:

        1. 使用async/await关键字:python3.5及以后出现,到目前为止这是目前最主流的 实现协程的方法。

        2. 使用yield关键字:使用yield关键字及其send方法可以实现协程的效果。

        3. 使用asyncio.coroutine:在python3.4发布之后可以使用该装饰器与yield from 配合来实现协程,不过在python3.8弃用。

        4. 使用第三方库:通过其他的第三方库也可以实现协程,如greenlet。

def consumer():print("消费者准备接收数据。")while True:# 接收生产者发送的数据data = (yield)print("消费者接收到了数据:", data)
def producer(consumer_generator):# 启动生成器,使它准备好接收数据next(consumer_generator)for i in range(5):print("生产者发送数据:", i)# 发送数据给消费者consumer_generator.send(i)# 终止生成器consumer_generator.close()
if __name__ == '__main__':# 创建消费者生成器consumer_coroutine = consumer()# 创建生产者producer(consumer_coroutine)
'''
消费者准备接收数据。
生产者发送数据: 0
消费者接收到了数据: 0
生产者发送数据: 1
消费者接收到了数据: 1
生产者发送数据: 2
消费者接收到了数据: 2
生产者发送数据: 3
消费者接收到了数据: 3
生产者发送数据: 4
消费者接收到了数据: 4
'''

2.1、async

async关键字是Python异步编程的核心组成部分,用于定义协程函数。协程函数与 普通函数不同,它们在调用时不会执行函数里面的代码,而是会返回一个协程对象。

# 定义一个协程函数
async def func():print('123')# 直接调用协程函数会发出警告,并且函数内部的功能也不会执行
func()

想要运行函数体里面的代码,需要进行两个方面的准备:

        1. 获取事件循环。

        2. 将协程对象封装为Task对象并提交到事件循环中。 

2.2、await

        在Python中, await关键字用于挂起(暂停)异步函数的执行,直到被等待的协程 (coroutine)完成。这是异步编程中的一个关键概念,因为它允许程序在等待结果 的同时执行其他任务。

2.2.1、await的基本用法

1. 只能在异步函数内部使用: await关键字只能在一个使用了 异步函数内部使用。它不能在普通的同步函数中使用。

2. 等待协程: async def定义的 await后面通常跟一个协程对象(一个异步函数的调用)。当执行到 await时,当前协程会暂停执行,等待右侧的协程完成。

2.2.2、await的工作原理

1. 挂起与恢复: 当执行到 await时,当前协程会挂起,并让出控制权给事件循环 (event loop)。事件循环可以在这段时间内运行其他协程或处理其他事件。一 旦await后面的协程完成,事件循环会恢复执行原来的协程, 果就是协程的返回值。

2. 非阻塞: 尽管 await表达式的结 await看起来像是同步代码中的阻塞操作,但实际上它是非阻塞 的。这是因为事件循环负责协程之间的切换,从而实现并发。

import asyncio
import time
# 定义一个异步函数say_after,它接受延迟时间和要打印的消息作为参数
async def say_after(delay, what):
# 使用await关键字挂起当前协程,直到指定的延迟时间结束后再继续执行await asyncio.sleep(delay)# 打印消息print(what)
# 定义主异步函数main
async def main():# 记录开始时间print(f"started at {time.strftime('%X')}")# 调用say_after函数,等待1秒后打印'hello'await say_after(1, 'hello')# 调用say_after函数,等待2秒后打印'world'# 注意:这里的执行不是并行的,而是顺序的,因为两个await语句是顺序执行的await say_after(2, 'world')# 记录结束时间print(f"finished at {time.strftime('%X')}")
# 调用asyncio.run()来启动主协程
# 这将创建一个新的事件循环并运行main()直到完成
asyncio.run(main())
'''
started at xx:xx:xx
hello
world
finished at xx:xx:xx
'''

三、事件循环

        事件循环是一种处理程序执行、事件和消息分发的机制。它不断地等待事件的发生, 当事件发生时,事件循环会将其分发给相应的处理程序进行处理。事件循环的核心是 一个循环,它会不断地检查是否有事件需要处理,如果有,就调用相应的回调函数来 处理这些事件。

其工作流程为:

1. 启动:创建并启动事件循环。

2. 注册事件:将各种事件(如网络套接字、文件描述符、定时器等)注册到事件循 环中。

3. 事件循环:进入一个循环,等待事件的发生,并处理这些事件。

4. 执行任务:当事件发生时,事件循环会调用相关的处理函数或恢复相应的协程。 

5. 关闭:当所有任务完成后,关闭事件循环。

事件循环的创建随着Python版本的不同而不同,在Python3.7版本之前,事件循环需 要先使用 asyncio.get_event_loop()来获取循环,然后使用 run_until_complete()来执行任务。在Python3.7及以后的版本,直接使用 asyncio.run()来直接执行任务。

import asyncio
# 定义一个异步函数func1
async def func1():print('start func1')  # 打印信息,表示func1开始执行await asyncio.sleep(2)  # 模拟异步I/O操作,协程在这里挂起2秒钟print('end func1')  # 打印信息,表示func1执行结束
# 定义另一个异步函数func2
async def func2():print('start func2')  # 打印信息,表示func2开始执行await asyncio.sleep(2)  # 模拟异步I/O操作,协程在这里挂起2秒钟print('end func2')  # 打印信息,表示func2执行结束
asyncio.run(func1())
asyncio.run(func2())
'''
start func1
end func1
start func2
end func2
'''

四、Task对象

        Task对象是 asyncio库中的一个实现,它用来在事件循环中安排协程的执行。一个 Task是对协程的一个封装,简单来说,协程本身并不会自动运行,当一个协程被封 装为一个Task对象并提交到事件循环中时,它才会在事件循环中被安排执行。当协程 执行完毕后, Task会提供协程的返回值或异常,并且相比协程对象, 更加丰富的方法供我们使用。

        将协程对象封装为 Task对象拥有 Task是通过asyncio库中的函数进行的,但随着Python版本的不 同,其所用函数也不同。

        在python3.7之前,Task的创建使用的是 asyncio.ensure_future()函数,通过该 函数将使用 async定义的协程函数所返回的协程对象提交到事件循环中。在 python3.7之后,创建Task对象的方法变得更加直接和明确,可以使用 asyncio.create_task()函数来创建,且python3.8版本之后,添加了name参数可 以为任务指定名称。这个函数接受一个协程对象作为参数,并返回一个新的Task对 象。

import asyncio
# 定义一个异步函数func1
async def func1():print('start func1')  # 打印信息,表示func1开始执行await asyncio.sleep(2)  # 模拟异步I/O操作,协程在这里挂起2秒钟print('end func1')  # 打印信息,表示func1执行结束
# 定义另一个异步函数func2
async def func2():print('start func2')  # 打印信息,表示func2开始执行await asyncio.sleep(2)  # 模拟异步I/O操作,协程在这里挂起2秒钟print('end func2')  # 打印信息,表示func2执行结束
# 定义主异步函数main,它将作为程序的入口点
async def main():# 创建任务列表,使用asyncio.create_task来创建任务tasks = [asyncio.create_task(func1()),  # 创建并调度func1作为异步任务asyncio.create_task(func2())# 创建并调度func2作为异步任务]# 使用asyncio.wait等待所有任务完成# asyncio.wait接收一个任务列表,并等待这些任务完成done, pending = await asyncio.wait(tasks)
# 使用 asyncio.run() 来运行主函数
# asyncio.run()是Python 3.7引入的,它会创建一个新的事件循环,运行传入的协程,并在协程完成后关闭事件循环
# 等同于asyncio.get_event_loop().run_until_complete(main())
asyncio.run(main())
'''
start func1
start func2
end func1
end func2
'''
import asyncio
# 定义一个异步函数func1
async def func1():print('start func1')  # 打印信息,表示func1开始执行await asyncio.sleep(2)  # 模拟异步I/O操作,协程在这里挂起2秒钟print('end func1')  # 打印信息,表示func1执行结束
# 定义另一个异步函数func2
async def func2():print('start func2')  # 打印信息,表示func2开始执行await asyncio.sleep(2)  # 模拟异步I/O操作,协程在这里挂起2秒钟print('end func2')  # 打印信息,表示func2执行结束
# 定义主异步函数main,它将作为程序的入口点
async def main():# 直接调用asyncio.gather不需要将协程对象先手动封装为Task对象# 该函数会负责将它们作为任务调度到事件循环中# asyncio.gather会返回一个包含所有结果的列表await asyncio.gather(func1(), func2())
# 使用 asyncio.run() 来运行主函数
# asyncio.run()会创建一个新的事件循环,运行传入的协程,并在协程完成后关闭事件循环
asyncio.run(main())
'''
start func1
start func2
end func1
end func2
'''

五、协程间通信

        与线程相似,协程之间的通信也只有消息队列一种,且拥有不同种类的消息队列。 在python中,协程所使用的消息队列在 asyncio.Queue库下,其中共存在三种类型 的队列,分别为标准的先进先出队列 Queue、先进后出队列 LifoQueue和优先级队 列PriorityQueue。

5.1、Queue 

先进先出的原则

asyncio.Queue(maxsize=0)

maxsize:队列的最大尺寸,如果maxsize小于等于零,则队列尺寸是无限的。如果 是大于0的整数,则当队列达到maxsize时, await put()将阻塞至某个元素被 get()取出。

类方法 :

●        Queue.qsize():返回队列中当前有几条消息。

●        Queue.empty():如果队列为空,返回True,否则返回 False。

●        Queue.full():如果队列已满(达到最大尺寸),返回 True,否则返回 False。

●        Queue.put(item, block=True, timeout=None):将 item 放入队列。如果 block 是True是 None(默认),则在必要时阻塞至有空闲的插槽, 如果timeout 是正数,将最多阻塞 timeout 秒,如果在这段时间内没有可 用的插槽,将引发 queue.Full 异常。 

●        Queue.put_nowait(item):相当于 Queue.put(item, block=False)。如果队列已满,立即引发 queue.Full 异常。

●        Queue.get(b1ock=True,timeout=None):从队列中移除并返回一个元素。如果 block是 True 且 timeout是 None(默认),则在必要时阻塞至队列中有项目可用。如果 timeout 是正数,将最多阻塞 timeout 秒,如果在这段时间内没有项目可用,将引发 queue.Empty 异常。

●        Queue.get_nowait():相当于 Queue.get(block=False)。如果队列为空立即引发 queue.Empty 异常。

 ●       Queue.task_done():指示之前入队的一个任务已经完成。由队列的消费者线程使用。每个Queue. get()调用之后,需要调用 Queue.task_done()告诉队列该任务处理完成。
●        Queue.join():阻塞直到队列中的所有元素都被处理完。当元素添加到队列的 时候,未完成任务的计数就会增加,每当消费协程调用 task_done()表示这个元 素已经处理完毕,那么未完成计数就会减少。当未完成计数降到零的时候, join()阻塞被接触。

import asyncio
# 生产者协程,负责生成一系列数字并将它们放入队列中
async def producer(queue, n):for i in range(1, n + 1):  # 循环从1到n,生成数字print(f'生产者生产了: {i}')  # 打印当前生产的数字await queue.put(i)  # 将数字放入队列,如果队列已满,则阻塞直到有空位await asyncio.sleep(1)  # 模拟生产耗时,等待1秒钟print('生产者完成生产。')  # 所有数字生产完毕,打印完成消息await queue.put(None)  # 放入一个None作为结束信号,通知消费者没有更多数字
# 消费者协程,负责从队列中取出数字并打印它们
async def consumer(queue):while True:  # 无限循环,直到接收到结束信号item = await queue.get()  # 从队列中取出一个元素,如果队列为空,则阻塞直到有元素if item is None:  # 检查是否接收到结束信号queue.task_done()  # 通知队列,当前任务已经完成break  # 如果是结束信号,退出循环print(f'消费者消费了: {item}')  # 打印当前消费的数字queue.task_done()  # 通知队列,当前任务已经完成print('消费者完成消费。')  # 打印完成消费的消息
# 主协程,负责启动生产者和消费者协程,并等待它们完成
async def main():queue = asyncio.Queue(5)  # 创建一个队列实例,用于生产者和消费者之间的通信# 创建生产者和消费者协程producer_coro = producer(queue, 5)  # 生产者协程,生产1到5的数字consumer_coro = consumer(queue)  # 消费者协程# 使用asyncio.gather等待生产者和消费者协程完成# gather允许同时运行多个协程,并在它们都完成时返回结果await asyncio.gather(producer_coro, consumer_coro)
# 运行主协程,启动事件循环
asyncio.run(main())
'''
生产者生产了: 1
消费者消费了: 1
生产者生产了: 2
消费者消费了: 2
生产者生产了: 3
消费者消费了: 3
生产者生产了: 4
消费者消费了: 4
生产者生产了: 5
消费者消费了: 5
生产者完成生产。
消费者完成消费。
'''

5.2、LifoQueue 

后进先出

asyncio.LifoQueue(maxsize=0)

maxsize:队列的最大尺寸。如果设置为小于或等于0的数,则队列的尺寸是无 限的。 

常用方法: 

●        LifoQueue.put(item, block=True, timeout=None):将 如果 block 是 True 且 timeout 是 item 放入队列。 None(默认),则在必要时阻塞至有空闲 的插槽。如果 timeout 是正数,将最多阻塞 timeout 秒,如果在这段时间内没 有可用的插槽,将引发完全异常。 
●        LifoQueue.put_nowait(item):相当于LifoQueue.put(item,b1ock=False)。如果队列已满,立即引发完全异常。
●        LifoQueue.get(block=True,timeout=None):从队列中移除并返回一个元素。如果 block是True且timeout是 None(默认),则在必要时阻塞至队列中有项目可用。如果 timeout 是正数,将最多阻塞timeout秒,如果在这段时间内没有项目可用,将引发完全异常。 
●        LifoQueue.get_nowait():相当于 LifoQueue.get(block=False)。如果队列为空,立即引发完全异常。
●        LifoQueue.qsize():返回队列中的项目数量
●        LifoQueue.empty():如果队列为空,返回 True,否则返回 False。
●        LifoQueue.full():如果队列已满(达到最大尺寸),返回 True,否则返回False 。

●        LifoQueue.task_done(): 指示之前入队的一个任务已经完成,即 get出来的元素相关操作已经完成。由队列中的 get()端掌控,每次 get用于一个任务时,任务最后要调用 task_done()告诉队列,任务已经完成。

●        LifoQueue.join():阻塞直到队列中的所有元素都被处理完。当元素添加到队 列的时候,未完成任务的计数就会增加,每当消费协程调用 task_done()表示这 个元素已经处理完毕,那么未完成计数就会减少。当未完成计数降到零的时候, join()阻塞被接触。

import asyncio
# 生产者协程,负责生成一系列数字并将它们放入队列中
async def producer(queue, n):for i in range(1, n + 1):  # 循环从1到n,生成数字print(f'生产者生产了: {i}')  # 打印当前生产的数字await queue.put(i)  # 将数字放入队列,如果队列已满,则阻塞直到有空位await asyncio.sleep(1)  # 模拟生产耗时,等待1秒钟print('生产者完成生产。')  # 所有数字生产完毕,打印完成消息await queue.put(None)  # 放入一个None作为结束信号,通知消费者没有更多数字
# 消费者协程,负责从队列中取出数字并打印它们
async def consumer(queue):await asyncio.sleep(5)while True:  # 无限循环,直到接收到结束信号item = await queue.get()  # 从队列中取出一个元素,如果队列为空,则阻塞直到有元素if item is None:  # 检查是否接收到结束信号queue.task_done()  # 通知队列,当前任务已经完成break  # 如果是结束信号,退出循环print(f'消费者消费了: {item}')  # 打印当前消费的数字queue.task_done()  # 通知队列,当前任务已经完成print('消费者完成消费。')  # 打印完成消费的消息
# 主协程,负责启动生产者和消费者协程,并等待它们完成
async def main():queue = asyncio.LifoQueue(10)  # 创建一个队列实例,用于生产者和消费者之间的通信# 创建生产者和消费者协程producer_coro = producer(queue, 5)  # 生产者协程,生产1到5的数字consumer_coro = consumer(queue)  # 消费者协程# 使用asyncio.gather等待生产者和消费者协程完成# gather允许同时运行多个协程,并在它们都完成时返回结果await asyncio.gather(producer_coro, consumer_coro)# 等待队列中的所有项目都被处理完毕await queue.join()print('所有任务都已处理完毕。')
# 运行主协程,启动事件循环
asyncio.run(main())
'''
生产者生产了: 1
生产者生产了: 2
生产者生产了: 3
生产者生产了: 4
生产者生产了: 5
消费者消费了: 5
消费者消费了: 4
消费者消费了: 3
消费者消费了: 2
消费者消费了: 1
生产者完成生产。
消费者完成消费。
所有任务都已处理完毕。
'''

5.3、PriorityQueue

实现优先级队列

asyncio.PriorityQueue(maxsize=0) 

maxsize:队列的最大尺寸。如果设置为小于或等于0的数,则队列的尺寸是无 限的。

常用方法: 

●        PriorityQueue.put((priority, item), block=True, timeout=None):将 item 放入队列,并为其指定一个优先级 timeout 是 priority。如果 block 是 True 且 None(默认),则在必要时阻塞至有空闲的插槽。如果 timeout 是正数,将最多阻塞 timeout 秒,如果在这段时间内没有可用的插槽,将引发 完全异常。
●        PriorityQueue.put_nowait((item, priority):相当于 PriorityQueue.put((item, priority), block=False)。如果队列已满,立 即引发完全异常。
●        PriorityQueue.get(block=True, timeout=None):从队列中移除并返回一 个元素。如果 block 是 True 且 timeout 是 None(默认),则在必要时阻塞 至队列中有项目可用。如果 timeout 是正数,将最多阻塞 timeout 秒,如果在 这段时间内没有项目可用,将引发完全异常。
●        PriorityQueue.get_nowait():相当于 PriorityQueue.get(block=False)。如果队列为空,立即引发完全异常。
●        PriorityQueue.qsize():返回队列中的项目数量。
●        PriorityQueue.empty():如果队列为空,返回True,否则返回False。
●        PriorityQueue.full():如果队列已满(达到最大尺寸),返回True,否则返回 False。 
●        PriorityQueue.task_done():指示之前入队的一个任务已经完成,即 来的元素相关操作已经完成。由队列中的 get()端掌控,每次get用于一个任务 时,任务最后要调用 get出 task_done()告诉队列,任务已经完成。 

import asyncio
# 生产者协程,负责生成一系列数字(这里实际上是字典键值对)并将它们放入队列中
async def producer(queue, n):fx = {4: 'd', 5: 'e', 2: 'b', 3: 'c', 1: 'a'}  # 定义一个字典,包含数字和字母的映射fx_tuples = [(key, value) for key, value in fx.items()]  # 将字典转换为元组列表for i in range(0, n):  # 循环从0到n-1,但由于字典是无序的,这里的i仅用作索引限制# 注意:如果n大于fx_tuples的长度,将会引发IndexErrorprint(f'生产者生产了: {fx_tuples[i]}')  # 打印当前生产的元组await queue.put(fx_tuples[i])  # 将元组放入队列,如果队列已满,则阻塞直到有空位await asyncio.sleep(1)  # 模拟生产耗时,等待1秒钟print('生产者完成生产。')  # 所有指定的元组生产完毕,打印完成消息await queue.put(None)  # 放入一个None作为结束信号,通知消费者没有更多元组
# 消费者协程,负责从队列中取出元组并打印它们
async def consumer(queue):await asyncio.sleep(5)  # 消费者在开始消费前等待5秒(模拟其他任务或延迟)while True:  # 无限循环,直到接收到结束信号item = await queue.get()  # 从队列中取出一个元素,如果队列为空,则阻塞直到有元素if item is None:  # 检查是否接收到结束信号queue.task_done()  # 通知队列,当前任务(即处理None作为结束信号的任务)已经完成break  # 如果是结束信号,退出循环print(f'消费者消费了: {item}')  # 打印当前消费的元组queue.task_done()  # 通知队列,当前任务(即处理一个元组的任务)已经完成print('消费者完成消费。')  # 打印完成消费的消息
# 主协程,负责启动生产者和消费者协程,并等待它们完成
async def main():queue = asyncio.PriorityQueue(10)  # 创建一个优先队列实例,用于生产者和消费者之间的通信,容量为10# 创建生产者和消费者协程producer_coro = producer(queue, 5)  # 生产者协程,尝试生产前5个字典中的键值对(注意字典无序)consumer_coro = consumer(queue)  # 消费者协程# 使用asyncio.gather等待生产者和消费者协程完成# gather允许同时运行多个协程,并在它们都完成时返回结果(这里不关心具体返回值)await asyncio.gather(producer_coro, consumer_coro)# 等待队列中的所有项目都被处理完毕(即等待所有task_done()被调用)await queue.join()print('所有任务都已处理完毕。')
# 运行主协程,启动事件循环
asyncio.run(main())
'''
生产者生产了: (4, 'd')
生产者生产了: (5, 'e')
生产者生产了: (2, 'b')
生产者生产了: (3, 'c')
生产者生产了: (1, 'a')
消费者消费了: (1, 'a')
消费者消费了: (2, 'b')
消费者消费了: (3, 'c')
消费者消费了: (4, 'd')
消费者消费了: (5, 'e')
生产者完成生产。
消费者完成消费。
所有任务都已处理完毕。
'''

六、协程同步

与进程、线程类似,协程也有同步机制,包括Lock、Semaphore、Event、 Condition。

6.1、Lock

在协程中,可以使用Lock来确保同一时间只有一个协程可以访问某个资源。

asyncio.Lock() 

其方法为:

        acquire():获取锁。此方法会等待直至锁为unlocked,然后将其设为locked并 返回True。当有一个以上的协程在 acquire()中被阻塞则会等待解锁,最终只 有一个协程会被执行。锁的获取是公平的,被执行的协程将是第一个开始等待锁 的协程。

        release():释放锁。当锁为locked时,将其设为unlocked并返回。如果锁为 unlocked,则会抛出异常。

        locked():如果锁为locked则返回 True。 

为了避免死锁,建议使用async with 来管理Lock。 

import asyncio  # 导入asyncio模块,提供异步编程的原语和工具
async def worker(lock, id):  # 定义一个协程函数,接收一个锁和一个标识符while True:  # 无限循环,模拟持续工作的协程async with lock:  # 使用async with语句获取锁,确保同一时间只有一个协程执行这部分代码print(f"Worker {id} is working")  # 打印当前协程正在工作的消息await asyncio.sleep(1)  # 模拟I/O操作,挂起协程1秒钟
async def main():  # 定义主协程函数,用于启动和管理其他协程lock = asyncio.Lock()  # 创建一个锁,用于同步协程,防止它们同时执行某些代码块# 创建两个协程,并将它们传递给asyncio.gather()函数# asyncio.gather()用于并发运行多个协程,并等待它们全部完成await asyncio.gather(worker(lock, 1), worker(lock, 2))  # 并发运行两个worker协程
asyncio.run(main())  # 运行主协程,启动事件循环并执行主协程

6.2、Semaphore

        在协程中,可以使用Semaphore来控制对资源的访问数量。Semaphore会管理一个 内部计数器,该计数器会随每次 acquire调用递减并随每次 release调用递增,计 数器的值永远不会降到零以下。当 acquire发现其值为零时,它将保持阻塞直到有某 个任务调用了 release。

asyncio.Semaphore(value=1) 

value:value参数用来为内部计数器赋初始值,默认为1。如果给定的值小于0则会 抛出异常。 

其方法为: 

        acquire():获取一个信号量。如果内部计数器的值大于零,则将其减一并立即 返回True。如果其值为零,则会等待直到 release被调用。

        release():释放一个信号量,将内部计数器的值加一。可以唤醒一个正在等待 获取信号量对象的任务。 

        locked():如果信号量对象无法被立即获取则返回True

建议使用async with来管理Semaphore。 

import asyncio
async def car(semaphore, car_id):"""模拟车辆进入停车场并离开的过程"""print(f"Car {car_id} 正在等待停车位")async with semaphore:  # 获取信号量,相当于获取停车位print(f"Car {car_id} 进入停车场了.")await asyncio.sleep(2)  # 模拟车辆在停车场内停留的时间print(f"Car {car_id} 离开停车场了")
# 信号量在退出async with块时自动释放,相当于车辆离开停车场
async def main():# 假设停车场只有3个停车位parking_spaces = asyncio.Semaphore(3)# 创建5个车辆协程cars = [car(parking_spaces, i) for i in range(1, 6)]# 并发运行所有车辆协程await asyncio.gather(*cars)
asyncio.run(main())
'''
Car 1 正在等待停车位
Car 1 进入停车场了.
Car 2 正在等待停车位
Car 2 进入停车场了.
Car 3 正在等待停车位
Car 3 进入停车场了.
Car 4 正在等待停车位
Car 5 正在等待停车位
Car 1 离开停车场了
Car 2 离开停车场了
Car 3 离开停车场了
Car 4 进入停车场了.
Car 5 进入停车场了.
Car 4 离开停车场了
Car 5 离开停车场了
'''

6.3、Event

        在python中使用Event允许一个协程通知一个或多个协程某个事件已经发生。Event 对象会管理一个内部标志,可通过 set方法将其设为True并通过 设为False。 clear方法将其重 wait方法会阻塞直至该标志被设为True。该标志初始时会被设为 False。

asyncio.Event() 

其方法为: 

        wait():协程等待直至事件被设置。如果事件已被设置,则立即返回True,否则将阻塞直至另一个任务调用set()

        set():设置事件。所有等待事件被设置的任务将被立即唤醒。 

        clear():清空(取消设置)事件。通过wait()进行等待的任务现在将会阻塞直至set()方法被再次调用。

        is_set():如果事件已被设置则返回 True。

import asyncio
import random
async def producer(event, data):"""生产者协程,它在准备好数据后设置事件"""print(f"Producer is preparing data: {data}")time = random.uniform(0.5, 2)print(time)await asyncio.sleep(time)  # 模拟数据准备时间print(f"Producer has prepared data: {data}")event.set()  # 设置事件,表示数据已经准备好了print("Producer has notified the consumer.")
async def consumer(event):"""消费者协程,它在事件被设置后开始消费数据"""print("Consumer is waiting for data.")await event.wait()  # 等待事件被设置print("Consumer has received the notification and is consuming data.")# 模拟数据处理await asyncio.sleep(random.uniform(0.5, 2))print("Consumer has finished consuming data.")
async def main():# 创建一个事件对象event = asyncio.Event()# 创建生产者和消费者协程producer_coro = producer(event, "data1")consumer_coro = consumer(event)# 并发运行生产者和消费者协程await asyncio.gather(producer_coro, consumer_coro)
asyncio.run(main())
'''
Producer is preparing data: data1
1.4196680751620212
Consumer is waiting for data.
Producer has prepared data: data1
Producer has notified the consumer.
Consumer has received the notification and is consuming data.
Consumer has finished consuming data.
'''

6.4、Condition

         在python中允许协程等待某些条件成立,然后被通知恢复执行。在本质上, Condition 对象合并了 Event和 Lock 的功能。 多个 Condition 对象有可能共享一个 Lock,这允许关注于共享资源的特定状态的不同任务实现对共享资源的协同独占访 问。

asyncio.Condition(lock=None) 

        lock:lock参数必须为自己创建的 Lock对象或None,在后一种情况下会自动创建 一个新的Lock对象。

其方法为: 

        acquire():获取下层的锁。此方法会等待直至下层的锁为 unlocked,将其设为 locked 并返回 True。

        notify(n=1):唤醒最多 n 个正在等待此条件的任务(默认为 1 个)。如果没 有任务正在等待则此方法为空操作。锁必须在此方法被调用前被获取并在随后被 快速释放。 如果通过一个 unlocked 锁调用则会引发异常。

        locked():如果下层的锁已被获取则返回 True。

        notify_all():唤醒所有正在等待此条件的任务。此方法的行为类似于 notify,但会唤醒所有正在等待的任务。锁必须在此方法被调用前被获取并在 随后被快速释放。 如果通过一个 unlocked 锁调用则会引发异常。

        release():释放下层的锁。在未锁定的锁调用时,会引发异常。

        wait():等待直至收到通知。当此方法被调用时如果调用方任务未获得锁,则 会引发异常。这个方法会释放下层的锁,然后保持阻塞直到被 notify()或 notify_all()调用所唤醒。 一旦被唤醒,Condition 会重新获取它的锁并且此 方法将返回 True。

        wait_for(predicate):等待直到目标值变为 true。目标必须为一个可调用对 象,其结果将被解读为一个布尔值。 

建议使用async with来管理Condition。 

import asyncio
# 生产者函数,负责通知所有等待的消费者
async def producer(condition):while True:  # 无限循环,模拟持续的生产活动await condition.acquire()  # 获取条件变量的锁condition.notify_all()  # 通知所有等待的消费者condition.release()  # 释放条件变量的锁await asyncio.sleep(1)  # 暂停一秒,模拟生产活动的时间间隔
# 消费者函数,负责等待生产者的通知
async def consumer(condition, number):while True:  # 无限循环,模拟持续的消费活动await condition.acquire()  # 获取条件变量的锁print(f'{number}正在等待condition')  # 打印消费者正在等待的通知await condition.wait()  # 等待生产者的通知print(f'{number}已释放condition')  # 打印消费者收到通知后的消息condition.release()  # 释放条件变量的锁
# 主函数,负责启动生产者和消费者任务
async def main():condition = asyncio.Condition()  # 创建一个条件变量# 创建任务列表,包括一个生产者和多个消费者tasks = [asyncio.create_task(producer(condition)),  # 创建生产者任务asyncio.create_task(consumer(condition, 1)),  # 创建消费者任务,编号为1asyncio.create_task(consumer(condition, 2)),  # 创建消费者任务,编号为2asyncio.create_task(consumer(condition, 3)),  # 创建消费者任务,编号为3asyncio.create_task(consumer(condition, 4)),  # 创建消费者任务,编号为4asyncio.create_task(consumer(condition, 5)),  # 创建消费者任务,编号为5]# 等待所有任务完成,由于生产者是无限循环,这里实际上会无限等待await asyncio.wait(tasks)
# 运行主函数,启动事件循环
asyncio.run(main())

七、将协程分布到线程池/进程池中

        一般情况下,程序的异步开发要么使用协程,要么使用进程池或线程池,但是也会碰 到有一些情况需要既使用协程又使用进程池或线程池,而进程池、线程池 submit后 返回的 Future和协程的 Future又不是一回事,不能直接使用await,因此就需要进 行一个对象的转换。

        在Python中,可以通过 asyncio.wrap_future()来将一个 concurrent.futures.Future转化为asyncio.Future,这样就可以去使用协程的相关内容了。

import asyncio
import concurrent.futures
import time
# 这是一个普通函数
def func1():time.sleep(5)print('in func1')
# 这是一个普通函数
def func2():time.sleep(3)print('in func2')
async def main():
# 创建一个进程池with concurrent.futures.ProcessPoolExecutor() as pool:# 使用进程池提交任务future1 = pool.submit(func1)future2 = pool.submit(func2)# 将 concurrent.futures.Future 转换为 asyncio.Futureasync_future1 = asyncio.wrap_future(future1)async_future2 = asyncio.wrap_future(future2)# 使用 asyncio 的 await 等待结果result = await asyncio.gather(async_future1,async_future2)print(f"The result is {result}")
# 注意:进程就需要放到主模块中去执行
if __name__ == '__main__':asyncio.run(main())
'''
in func2
in func1
The result is [None, None]
'''

 使用 loop.run_in_executor()直接转换

        使用 asyncio.get_running_loop()时,如果当前没有正在运行的事件循环,就抛 出异常。而上面的 asyncio.get_event_loop()则是在当前没有正在运行的事件循 环的基础上,会创建一个新的事件循环。相对来说, asyncio.get_running_loop()更适合在协程或异步函数内部使用, asyncio.get_event_loop()适用于更广泛的情况。 

loop.run_in_executor(executor, func, *args): 

        executor:一个执行器对象,通常是 concurrent.futures.ThreadPoolExecutor 或 concurrent.futures.ProcessPoolExecutor 的实例。它管理同步函数的执 行,如果不指定就默认创建一个线程池。

        func:要执行的同步函数。

        *args:传递给 func的位置参数。 

import asyncio
import time# 示例同步函数,模拟耗时操作
def slow_function1():# 打印信息,表示函数开始执行print("Function 1 is running")# 模拟耗时操作,线程睡眠2秒time.sleep(2)# 打印信息,表示函数执行完毕print("Function 1 is done")# 返回函数执行结束的信息return 'func1 end'def slow_function2():# 打印信息,表示函数开始执行print("Function 2 is running")# 模拟耗时操作,线程睡眠2秒time.sleep(2)# 打印信息,表示函数执行完毕print("Function 2 is done")# 返回函数执行结束的信息return 'func2 end'async def main():# 获取当前正在运行的事件循环loop = asyncio.get_running_loop()print('before run')# 使用线程池执行器并发运行两个同步函数# run_in_executor的第一个参数为None,表示使用默认的线程池执行器task1 = loop.run_in_executor(None, slow_function1)task2 = loop.run_in_executor(None, slow_function2)print('after run')# 等待两个函数执行完成,并获取它们的返回值result1 = await task1result2 = await task2print('after await')# 打印两个函数的执行结果print(f"Result of function 1: {result1}")print(f"Result of function 2: {result2}")if __name__ == '__main__':# 记录程序开始执行的时间start = time.time()# 运行主函数asyncio.run(main())# asyncio.get_running_loop()# 打印程序执行的总时间print('total_time', time.time() - start)
'''
before run
Function 1 is running
Function 2 is running
after run
Function 1 is done
Function 2 is done
after await
Result of function 1: func1 end
Result of function 2: func2 end
total_time 2.0069408416748047
'''

八、思维导图

相关文章:

Python----Python高级(并发编程:协程Coroutines,事件循环,Task对象,协程间通信,协程同步,将协程分布到线程池/进程池中)

一、协程 1.1、协程 协程,Coroutines,也叫作纤程(Fiber) 协程,全称是“协同程序”,用来实现任务协作。是一种在线程中,比线程更加轻量级的存在,由程序员自己写程序来管理。 当出现IO阻塞时,…...

神经网络(Neural Network)

引言 神经网络,作为人工智能和机器学习领域的核心组成部分,近年来在诸多领域取得了显著的进展。受生物神经系统的启发,神经网络通过模拟人脑神经元的工作机制,能够从大量数据中学习复杂的模式和关系。其强大的非线性建模能力使其在图像识别、自然语言处理、语音识别和预测…...

AIGC-微头条爆款文案创作智能体完整指令(DeepSeek,豆包,千问,Kimi,GPT)

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列AIGC(GPT、DeepSeek、豆包、千问、Kimi)👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资…...

A股level2高频数据分析20250205

A股level2高频数据分析20250205 通过Level2的逐笔成交与委托记录,这种高精度的毫秒级数据能够洞察诸多重要信息,包括庄家目的、误导性行为,使所有交易操作透明化。这对于分析高手的交易策略极为有益,对机器学习的研究也极具价值&…...

【Pytorch实战教程】PyTorch中的Dataset用法详解

PyTorch中的Dataset用法详解 在深度学习中,数据是模型训练的基石。PyTorch作为一个强大的深度学习框架,提供了丰富的工具来处理和加载数据。其中,Dataset类是PyTorch中用于处理数据的重要工具之一。本文将详细介绍Dataset的用法,帮助你更好地理解和使用它。 1. 什么是Dat…...

UIAbility 生命周期方法

生命周期流程图 UIAbility的生命周期官方文档地址https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/uiability-lifecycle-V13 1. onCreate(want: Want, launchParam: LaunchParam) 触发时机:Ability首次创建时 作用:初始化核心资源…...

UML学习

定义:UML是一种用于软件系统分析和设计的标准化建模语言。 作用:用于描述系统的结构、行为、交互等。共定义了10种,并分为4类 ①用例图 user case diagram : 从外部用户的角度描述系统的功能,并指出功能的执行者. 静态图(②类图 class diagram ③,对象…...

DeepSeek-R1 32B Windows+docker本地部署

最近国产大模型DeepSeek兴起,本地部署了一套deepseek同时集成Open WebUI界面,给大家出一期教程。 软件:Ollama、docker、Open WebUI 一、用Ollama下载模型 首先我们需要安装Ollama,它可以在本地运行和管理大模型。 到Ollama官网 https://ol…...

界址点成果表批量生成(新增.docx格式)-附工具下载链接

界址点编号工具20250208更新(成果表新增.docx格式)。 【工具简介】工具可根据面图层,西北角顺时针批量生成界址点,可以设置角度、距离参数,来减少生成界址点的数量(不用全部节点生成界址点)。生…...

计算机组成原理(3)

计算机组成原理(3) 存储器层次结构存储器概述存储器分类存储器性能指标 半导体随机存储SRAM和DRAM 存储器层次结构 主存-辅存:实现了虚拟存储系统,解决了主存容量不足的问题; Cache-主存:解决了主存于CPU速…...

【新书速荐】《Information-Theoretic Radar Signal Processing(信息论雷达信号处理)》

引言 最近,由Yujie Gu 博士和 Yimin D. Zhang 教授主编的新书 Information-Theoretic Radar Signal Processing由 Wiley-IEEE Press 正式出版。 这是信息论雷达信号处理领域的首部专著,全书共分 14 章,汇集了来自学术界、工业界和政府机构的…...

ES冷热数据分离配置

冷热数据是根据索引创建时间来进行迁移的。一旦迁移到冷数据节点,则无法再恢复成热数据,因为热数据节点中该索引已经没有分片存在了。 基于Docker搭建ES集群,并设置冷热数据节点 配置冷热数据迁移策略 PUT https://192.168.x.xx:19200/_ilm/policy/my…...

Docker 构建镜像并搭建私人镜像仓库教程

构建镜像教程 步骤 1:安装 Docker #在安装 Docker 之前,建议先更新系统软件包。 sudo yum update -y # 移除旧的Docker版本和Podman、runc软件包及其相关依赖。 yum remove -y docker docker-client docker-client-latest docker-ce-cli docker-commo…...

蓝牙耳机降噪进化:从物理阻隔到声波抵消的跨越

目录 一、降噪技术的核心分类(一)被动降噪:物理屏障的“静音盾”(二)主动降噪:声波抵消的“黑科技”(三)混合降噪:双重技术的“强强联合” 二、细分技术及应用场景&#…...

23.PPT:校摄影社团-摄影比赛作品【5】

目录 NO12345​ NO6 NO7/8/9/10​ 单元格背景填充表格背景填充文本框背景填充幻灯片背景格式设置添加考生文件夹下的版式 NO12345 插入幻灯片和放入图片☞快速:插入→相册→新建相册→文件→图片版式→相框形状→调整边框宽度左下角背景图片:视图→…...

systemverilog的program和module的区别

1. 设计目的 module 硬件建模:用于描述数字电路的结构和行为(如组合逻辑、时序逻辑、连线等)。 层次化设计:支持模块化设计,可嵌套其他模块或接口(interface)。 仿真周期内持续存在&#xff1…...

AI大模型零基础学习(2):提示词工程进阶——让AI听懂你的“弦外之音“

从“基础对话”到“精准操控”的思维跃迁 一、为什么你的提问总被AI“误解”? 1.1 大模型的“思维盲区” 知识边界:ChatGPT等模型的训练数据截止于特定时间(如2023年1月),无法主动获取最新资讯 文化差异:…...

如今物联网的快速发展对hmi的更新有哪些积极影响

一、功能更加丰富 物联网的快速发展使得 HMI(人机界面)能够连接更多的设备和系统,从而实现更加丰富的功能。例如,通过与传感器网络的连接,HMI 可以实时显示设备的运行状态、环境参数等信息,为用户提供更加…...

基于yolov11的阿尔兹海默症严重程度检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv11的阿尔兹海默症严重程度检测系统是一种创新的医疗辅助工具,旨在通过先进的计算机视觉技术提高阿尔兹海默症的早期诊断和病情监测效率。阿尔兹海默症是一种渐进性的神经退行性疾病,通常表现为认知障碍、记忆丧失和语言障碍等症状…...

redis之GEO 模块

文章目录 背景GeoHash 算法redis中的GeoHash 算法基本使用增加距离获取元素位置获取元素的 hash 值附近的元素 注意事项原理 背景 如果我们有需求需要存储地理坐标,为了满足高性能的矩形区域算法,数据表需要在经纬度坐标加上双向复合索引 (x, y)&#x…...

Java/Kotlin双语革命性ORM框架Jimmer(一)——介绍与简单使用

概览 Jimmer是一个Java/Kotlin双语框架 包含一个革命性的ORM 以此ORM为基础打造了一套综合性方案解决方案,包括 DTO语言 更全面更强大的缓存机制,以及高度自动化的缓存一致性 更强大客户端文档和代码生成能力,包括Jimmer独创的远程异常 …...

如何修改DNS解析?

DNS(域名系统)就像互联网的“电话簿”,负责将我们输入的网址转换为计算机能够理解的IP地址。如果DNS解析出现问题,访问网站就会受到影响。那我们该如何修改DNS解析呢?接下来,我们就来介绍一下这个话题。 为什么要修改DNS解析? 使用默认的…...

使用 POI-TL 和 JFreeChart 动态生成 Word 报告

文章目录 前言一、需求背景二、方案分析三、 POI-TL JFreeChart 实现3.1 Maven 依赖3.3 word模板设置3.2 实现代码 踩坑 前言 在开发过程中,我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart,实现动态生成 W…...

Python的那些事第十五篇:数据分析中的“三剑客”NumPy、Pandas与Matplotlib

数据分析中的“三剑客”:NumPy、Pandas与Matplotlib 在数据分析的世界里,有三位“大侠”常常携手并肩,共同应对各种复杂的数据挑战。它们就是NumPy、Pandas和Matplotlib。这三位“大侠”各怀绝技,相互配合,让数据分析…...

25/2/7 <机器人基础> 牛顿-欧拉递推公式,开闭环

牛顿-欧拉递推公式是用于计算刚体动力学中,刚体的角速度和角加速度的递推关系。这个公式是牛顿第二定律和欧拉旋转定理的结合,适用于描述刚体在空间中的旋转运动。 对于一个刚体,设其在某时刻的角速度为 ω,角加速度为 α&#xf…...

vue3父子组件传值方式

父子组件传值方式 在 Vue 3 中,父子组件之间传值主要有以下几种常见方式,下面将详细介绍并给出演示代码。 1. 父组件向子组件传值:使用 props 原理 props 是 Vue 中用于在父组件向子组件传递数据的机制。父组件通过在子组件标签上绑定属性…...

树和二叉树_6

树和二叉树_6 一、leetcode-105二、题解1.引库2.代码 一、leetcode-105 从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节…...

使用wpa_supplicant和wpa_cli 扫描wifi热点及配网

一:简要说明 交叉编译wpa_supplicant工具后会有wpa_supplicant和wpa_cli两个程序生产,如果知道需要连接的wifi热点及密码的话不需要遍历及查询所有wifi热点的名字及信号强度等信息的话,使用wpa_supplicant即可,否则还需要使用wpa_…...

个人职业发展——效率为王:AI赋能前端开发

在竞争激烈的IT行业,个人职业发展至关重要。而提升工作效率,无疑是加速职业发展的关键引擎。对于前端开发者而言,面对日益复杂的项目需求、不断变化的技术栈以及大量重复性的工作,如何提升效率,成为一个亟待解决的难题…...

【Android】Android开发应用如何开启任务栏消息通知

Android开发应用如何开启任务栏消息通知 1. 获取通知权限2.编写通知工具类3. 进行任务栏消息通知 1. 获取通知权限 在 AndroidManifest.xml 里加上权限配置&#xff0c;如下。 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android…...

c语言:取绝对值

假设我们有一个 long 类型的变量 l&#xff0c;我们希望恢复其绝对值。以下是两种方法的对比&#xff1a; 方法1&#xff1a;使用条件语句 这个很好理解&#xff0c;负数时取负运算 &#xff0c;用于数值的符号反转。 long abs_value(long l) {if (l < 0) {return -l;} e…...

HTML应用指南:利用GET请求获取全国盒马门店位置信息

随着新零售业态的发展&#xff0c;门店位置信息的获取变得至关重要。作为新零售领域的先锋&#xff0c;盒马鲜生不仅在商业模式创新上持续领先&#xff0c;还积极构建广泛的门店网络&#xff0c;以支持其不断增长的用户群体。本篇文章&#xff0c;我们将继续探究GET请求的实际应…...

MongoDB 深度教程:当 NoSQL 遇上幽默

MongoDB 深度教程&#xff1a;当 NoSQL 遇上幽默 欢迎来到这个 MongoDB 的奇妙冒险&#xff01;如果你已经了解了 MongoDB 的基础知识&#xff0c;比如 CRUD 操作、数据库和集合的概念&#xff0c;但总觉得自己只是停留在门口徘徊&#xff0c;那么今天这篇教程就是为你量身定做…...

windows安装linux子系统【ubuntu】操作步骤

1.在windows系统中开启【适用于Linux的Windows子系统】 控制面板—程序—程序和功能—启用或关闭Windows功能—勾选适用于Linux的Windows子系统–确定 2.下载安装Linux Ubuntu 22.04.5 LTS系统 Ununtu下载链接 3.安装完Ununtu系统后更新系统 sudo apt update4.进入/usr/l…...

通过k8s请求selfsubjectrulesreviews查询权限

当前是通过kubelet进行查询 curl --cacert /etc/kubernetes/pki/ca.crt \ --cert /var/lib/kubelet/pki/kubelet-client-current.pem \ --key /var/lib/kubelet/pki/kubelet-client-current.pem \ -d - \ -H "Content-Type: application/json" \ -H Accept: applicat…...

如何参与开源项目

目的 就是说一下如何参与开源的项目&#xff0c;通过参与QXlsx来说明开源项目是如何参与的&#xff0c;其它的github上的开源项目&#xff0c;也是这样的流程。 关于GitHub: GitHub是一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持Git作为唯一的版本库格式进行…...

Java 大视界 -- Java 大数据在智能金融监管中的应用与实践(77)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

使用deepseek快速创作ppt

目录 1.在DeekSeek生成PPT脚本2.打开Kimi3.最终效果 DeepSeek作为目前最强大模型&#xff0c;其推理能力炸裂&#xff0c;但是DeepSeek官方没有提供生成PPT功能&#xff0c;如果让DeepSeek做PPT呢&#xff1f; 有个途径&#xff1a;在DeepSeek让其深度思考做出PPT脚本&#xf…...

如何在Python中创建和访问列表

在Python中&#xff0c;列表&#xff08;list&#xff09;是一种用于存储一系列有序项目的数据结构。这些项目可以是不同类型的&#xff0c;比如整数、浮点数、字符串&#xff0c;甚至是其他列表&#xff08;形成嵌套列表&#xff09;。以下是如何在Python中创建和访问列表的基…...

多线程下jdk1.7的头插法导致的死循环问题

20250208 多线程下jdk1.7的头插法导致的死循环问题 多线程下jdk1.7的头插法导致的死循环问题 【新版Java面试专题视频教程&#xff0c;java八股文面试全套真题深度详解&#xff08;含大厂高频面试真题&#xff09;】 jdk1.7在hashmap扩容时使用的是头插法&#xff0c;所以扩容…...

低代码系统-插件功能分析( 某道云)

本文主要把其的插件进行了简单分析&#xff0c;不做业务上的梳理&#xff0c;不做推荐。 可大致分为&#xff1a; 群机器人 信息查询 智能识别 实名验证类 数据库类 通知类 通知类 aPaas增强 考勤同步 财务类 类别 插件名称 功能简介 群机器人类 某钉机器人 即在表单处完…...

手搓基于CNN的Chest X-ray图像分类

数据集Chest X-ray PD Dataset 数据集介绍 - 知乎https://zhuanlan.zhihu.com/p/661311561 CPU版本 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import Dataset, DataLoader from torchvision import transforms, models import …...

Golang 并发机制-7:sync.Once实战应用指南

Go的并发模型是其突出的特性之一&#xff0c;但强大的功能也带来了巨大的责任。sync.Once是由Go的sync包提供的同步原语。它的目的是确保一段代码只执行一次&#xff0c;而不管有多少协程试图执行它。这听起来可能很简单&#xff0c;但它改变了并发环境中管理一次性操作的规则。…...

java nio 底层使用的是select 、poll还是epoll

在 Java NIO 中&#xff0c;底层使用的 I/O 多路复用机制取决于操作系统和 JDK 的实现。 &#x1f680; Java NIO 底层使用机制概述 操作系统默认多路复用机制系统调用Linuxepollepoll_create、epoll_ctl、epoll_waitWindowsselect 或 WSAPollselect() 或 WSAPoll()macOS / B…...

Kotlin协程详解——协程取消与超时

目录 一、协程取消 1.取消协程的执行 2.使计算代码可取消 3.在finally中释放资源 4.运行不能取消的代码块 二、协程超时 异步超时与资源管理 一、协程取消 1.取消协程的执行 在一个长时间运行的应用程序中&#xff0c;你也许需要对你的后台协程进行细粒度的控制。 比如…...

双向链表、内核链表和gdb(20250208)

单向链表 节点 数据域 指针域&#xff08;后继节点&#xff09; 双向链表 相比于单项指针&#xff0c;双向指针存有前驱节点的地址&#xff0c;使链表的灵活性更高。 内核链表 在内核中 offsetof&#xff1a;获取结构体某个成员到结构体开头的偏移量container_of:根据结…...

全程Kali linux---CTFshow misc入门(38-50)

第三十八题&#xff1a; ctfshow{48b722b570c603ef58cc0b83bbf7680d} 第三十九题&#xff1a; 37换成1&#xff0c;36换成0&#xff0c;就得到长度为287的二进制字符串&#xff0c;因为不能被8整除所以&#xff0c;考虑每7位转换一个字符&#xff0c;得到flag。 ctfshow{5281…...

MySQL 8.0.41安装教程(2025年2月8号)

下载网址&#xff1a;https://www.mysql.com/cn/downloads/ 点击 我选择的是第二个离线安装 点击之后&#xff0c;选择直接下载&#xff1a; 下载完成双击&#xff1a; 我选择的是自定义安装&#xff1a; 右边默认已经存在我选择的8.0.41 点击红框中的&#xff0c;自定义安装路…...

【自学笔记】Deepseek的基础知识点总览-持续更新

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Deepseek知识点总览一、Deepseek简介二、Deepseek的三大适用模式1. 基础模型&#xff08;V3&#xff09;2. 深度思考&#xff08;R1&#xff09;3. 联网搜索模式 三…...

10N10-ASEMI中低压N沟道MOS管10N10

编辑&#xff1a;ll 10N10-ASEMI中低压N沟道MOS管10N10 型号&#xff1a;10N10 品牌&#xff1a;ASEMI 封装&#xff1a;TO-252 最大漏源电流&#xff1a;10A 漏源击穿电压&#xff1a;100V 批号&#xff1a;最新 RDS&#xff08;ON&#xff09;Max&#xff1a;135mΩ …...