Asynchronous I/O
Synchronous I/O 会阻塞以等待 I/O 操作完成,I/O 操作涉及数据在硬件上的移动,较内存上的电流改变,效率上低数个数量级。在等待 I/O 操作完成期间,处理器处于空闲状态。为了充分压榨处理器性能,在等待 I/O 操作完成期间,处理器可以处理与此次 I/O 无关的操作。这就是 Asynchronous I/O 的作用,Asynchronous I/O 不会阻塞,程序可以继续执行,当 I/O 操作完成后,OS 通知程序该 I/O 操作完成(基于设备状态轮询或者硬件中断)。
Asynchronous I/O 被用于提升效率,在某些情况下,也能提升吞吐量。然而,它在时延方面有消极的影响,有时吞吐量方面也是如此。
Forms
Blocking | Non-blocking | Asynchronous | |
---|---|---|---|
API | write, read | write, read + poll/select | aio_write, aio_read |
所有形式的异步 I/O 都涉及资源冲突和相关的问题。
Process
Select(/poll)
select loop 使用 select
系统调用进行休眠,等待文件描述符上的情况发生,超时发生,或接收到一个信号(比如,当子进程死亡)。通过检查 select 的返回参数,循环找出哪个文件描述符已经发生了改变并执行相应的代码。通常,为了易于使用,select loop 被实现为 event loop,或许会使用 callback functions;非常适用于 event-driven programming。
Signals
BSD 和 POSIX Unix 可用。I/O 被异步发起,当它完成时,signal(interrupt) 被生成。
Light-weight processes or threads
Light-weight processes(LWPs) 或 threads 在很多现代 Unixs 中可用。如同 process
方法,但是没有阻碍流之间合作的数据隔离。缺少隔离引入了它自己的问题,通常需要内核提供的同步机制和线程安全库。每个 LWP 或线程本身使用传统的阻塞同步 I/O。必须分离的线程栈可能妨碍使用非常大量线程的大规模实现。
Completion queues/ports
I/O 请求是异步发起的,但是完成的通知是通过同步队列机制,以 I/O 完成的的顺序提供的。通常与主进程(event-driven programming) 的状态机结构相关联, 这与不使用异步 I/O 或者使用其他形式的异步 I/O 没有一点相似之处,导致代码很难重用。不需要额外的同步机制或者线程安全库,文本(code)流和时间(event)流也不分离。
Implementation
绝大部分的通用目的的计算硬件完全依赖于两种异步 I/O 的实现方法:polling 和 interrupts。通常两种方法一起使用,
Examples
- 阻塞,同步:
device = IO.open()
data = device.read() # thread will be blocked until there is data in the device
print(data)
- 阻塞和非阻塞,同步
device = IO.open()
ready = False
while not ready:
print("There is no data to read!")
ready = IO.poll(device, IO.INPUT, 5) # returns control if 5 seconds have elapsed or there is data to read (INPUT)
data = device.read()
print(data)
- 非阻塞,异步
ios = IO.IOService()
device = IO.open(ios)
def inputHandler(data, err):
"Input data handler"
if not err:
print(data)
device.readSome(inputHandler)
ios.loop() # wait till all operations have been completed and call all appropriate handlers
Async/await:
ios = IO.IOService()
device = IO.open(ios)
async def task():
try:
data = await device.readSome()
print(data)
except:
pass
ios.addTask(task)
ios.loop() # wait till all operations have been completed and call all appropriate handlers
Reactor pattern:
device = IO.open()
reactor = IO.Reactor()
def inputHandler(data):
"Input data handler"
print(data)
reactor.stop()
reactor.addHandler(inputHandler, device, IO.INPUT)
reactor.run() # run reactor, which handles events and calls appropriate handlers
Overlapped I/O
Overlapped I/O 是 Windows API 中对于 asynchronous I/O 的名字。