python 并发编程(五)

 IO模型详解

  • 阻塞IO(blocking IO)

        在阻塞IO模型中,当一个进程需要从某个设备(例如硬盘)中读取数据时,它必须等待这个设备的数据准备好,然后才能继续执行。这个等待的过程就是阻塞,因为它阻止了进程的进一步执行。这种模型在早期的系统中非常常见,因为它相对简单,但是它的问题在于效率不高,因为CPU在等待IO操作完成的过程中可能会闲置。

  • 非阻塞IO(non-blocking IO)

        在非阻塞IO模型中,当一个进程请求IO操作时,它不会阻塞,而是立即返回。但是,这个操作可能还没有完成。因此,进程需要定期检查IO操作的进度,看是否已经准备好。如果还没有准备好,进程就去做其他的事情。这种模型可以让CPU更有效地利用起来,因为即使IO操作没有完成,进程也可以做其他的事情。但是,它也有缺点,因为它需要进程不断地检查IO操作的进度,这可能会增加CPU的负载。

  • 多路复用IO(IO multiplexing)

        多路复用IO模型是一种更高级的IO模型。在多路复用IO模型中,进程可以在等待某个IO操作时,同时等待多个其他的IO操作。如果有一个IO操作准备好了,进程就处理这个操作,然后继续等待其他的操作。这种模型可以有效地利用CPU,因为它允许多个IO操作同时进行。多路复用IO模型通常使用操作系统提供的一些特殊机制来实现,例如poll或select。

  • 异步IO(Asynchronous I/O)

异步IO模型是一种最先进的IO模型。在异步IO模型中,当一个进程请求一个IO操作时,它不会等待这个操作完成。相反,它只是简单地告诉操作系统:“我想做一个IO操作,你帮我完成它,然后通知我。”然后,进程就可以做其他的事情了。当操作系统完成IO操作时,它会通知进程。这种模型的最大优点是效率非常高,因为进程不需要等待IO操作完成就可以做其他的事情。然而,实现异步IO模型通常需要更复杂的编程技巧和操作系统支持。

  • asyncio简单介绍

以下是 asyncio 模块中一些常用的函数和参数的详解:

  1. asyncio.run(main): 这个函数是用来创建主事件循环并运行主程序的主要函数。参数 main 是一个协程对象,表示主程序。当调用 asyncio.run(main) 时,它会创建一个新的事件循环,然后运行 main 协程,直到 main 协程完成。在 main 协程结束后,事件循环会关闭。
  2. asyncio.get_event_loop(): 这个函数用于获取当前的主事件循环。如果当前线程没有事件循环,它会创建一个新的事件循环并返回。如果有多个线程同时调用这个函数,每个线程都会得到一个独立的事件循环。
  3. asyncio.set_event_loop(loop): 这个函数用于设置当前线程的事件循环为 loop。如果当前线程没有事件循环,或者 loop 是当前线程的事件循环,这个函数没有任何效果。否则,它会抛出 RuntimeError
  4. asyncio.sleep(delay): 这个函数用于挂起当前协程一段时间,类似于休眠。在休眠期间,这个协程会让出 CPU 给其他协程使用。它的参数 delay 是休眠的时间(秒)。当休眠结束时,它会返回一个新的 Future 对象,可以用来获取休眠的结果。
  5. asyncio.run_until_complete(coro)接受一个协程(coroutine)对象作为参数,并运行这个协程直到它完成。这个函数会阻塞当前线程,直到协程运行完毕。
#示例1
import threading
import asyncio

@asyncio.coroutine
def hello():
    print('Hello world! (%s)' % threading.currentThread())
    yield from asyncio.sleep(3)
    print('Hello again! (%s)' % threading.currentThread())

loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

"""
Hello world! (<_MainThread(MainThread, started 16812)>)
Hello world! (<_MainThread(MainThread, started 16812)>)
(暂停大约3秒)
Hello again! (<_MainThread(MainThread, started 16812)>)
Hello again! (<_MainThread(MainThread, started 16812)>)
"""
#示例2
import threading
import asyncio

async def hello():
    print('Hello world! (%s)' % threading.currentThread())
    await asyncio.sleep(3)
    print('Hello again! (%s)' % threading.currentThread())


async def main():
    task1 = asyncio.create_task(hello())
    task2 = asyncio.create_task(hello())
    # 等待所有任务完成
    await task1
    await task2
    # 所有任务完成,输出结果
    print("All operations completed")

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

"""
Hello world! (<_MainThread(MainThread, started 16812)>)
Hello world! (<_MainThread(MainThread, started 16812)>)
(暂停大约3秒)
Hello again! (<_MainThread(MainThread, started 16812)>)
Hello again! (<_MainThread(MainThread, started 16812)>)
"""
  • 对比asyncio库和threading库

import threading
import asyncio
import time

#基于协程的异步编程实现
@asyncio.coroutine
def hello():
    print('Hello world! (%s)' % threading.currentThread())
    yield from asyncio.sleep(3)
    print('Hello again! (%s)' % threading.currentThread())

loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()


#多线程实现

def hello():
    print('Hello world! (%s)' % threading.currentThread())
    time.sleep(3)
    print('Hello again! (%s)' % threading.currentThread())


if __name__ == '__main__':
    task1 = threading.Thread(target=hello)
    task2 = threading.Thread(target=hello)

    task1.start()
    task2.start()
    time.sleep(3)  # Wait for threads to finish
    task1.join()
    task2.join()
  1. 第一段代码使用了 asyncio 库,这是一个用于处理 I/O 操作的库,它支持基于协程的异步编程。协程是一种可以被暂停和恢复的函数,非常适合处理异步操作。在 asyncio 中,通过 async 和 await 关键词可以定义协程和暂停/恢复协程的执行。asyncio.coroutine 装饰器在 Python 3.4 中已被弃用,但在 Python 3.5 中,使用 async 和 await 代替。这段代码中的 asyncio.coroutine 和 @asyncio.coroutine 是等价的。

这段代码中,主线程会创建两个任务(协程),每个任务执行函数 hello,然后主线程等待这两个任务完成。这里使用了 asyncio.wait 来等待所有任务完成。

  1. 第二段代码使用了 threading 库,这是 Python 标准库中用于处理线程的库。线程是操作系统分配的最小的执行单元,每个线程都有自己的栈和局部变量。这段代码中,主线程会创建两个线程,每个线程执行函数 hello。然后主线程等待这两个线程完成。

这两种并发模型的主要区别在于:

  • asyncio 是单线程的,它主要用于 I/O 密集型任务,适合处理大量并发的小任务。
  • threading 是多线程的,它主要用于 CPU 密集型任务,适合处理大量并发的计算任务。

在性能上,这两种模型各有优缺点。多线程由于可以同时执行多个任务,因此在处理大量并发的计算任务时具有优势。但是创建线程的开销比较大,线程间的数据共享和通信也比较复杂。而异步IO由于其非阻塞的特性,可以更高效地利用CPU,因此在处理大量并发的I/O任务时具有优势。但是当需要等待某个任务完成时(例如等待磁盘I/O完成),异步IO可能会造成其他任务被挂起,从而降低整体的并发度。

补充:

I/O密集型任务是指那些需要大量输入输出操作的程序,例如网络服务器、数据库系统和文件传输等。这些应用程序通常需要高速的磁盘、网络和其他输入输出设备来完成任务,而CPU的运算能力相对较少。

CPU密集型任务是指那些需要大量CPU运算能力的程序,通常涉及到大量的数据处理和计算操作,例如科学计算、图像处理和加密解密等。这些应用程序通常需要高速的CPU和大量的内存来完成任务。

想了解更多关于asyncio模块知识可以看一下以下这篇文章:

python异步编程之asyncio(百万并发) - 三只松鼠 - 博客园 (cnblogs.com)

python异步库asyncio使用方法 - xyztank - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值