三重奏:从同一fd

时间:2018-09-18 12:28:00

标签: python python-trio

我有一个文件描述符,我想从中读取多个任务。 fd上的每个read()请求都将返回完整的独立数据包(只要数据可用)。

我幼稚的实现是让每个工作程序运行以下循环:

async def work_loop(fd):
   while True:
     await trio.hazmat.wait_readable(fd)
     buf = os.read(fd, BUFSIZE)
     if not buf:
         break
     await do_work(buf)

不幸的是,这不起作用,因为如果多个任务在同一fd上阻塞,则trio会引发ResourceBusyError。所以我的下一个迭代是编写一个自定义的等待函数:

async def work_loop(fd):
   while True:
     await my_wait_readable(fd)
     buf = os.read(fd, BUFSIZE)
     if not buf:
         break
     await do_work(buf)

其中

read_queue = trio.hazmat.ParkingLot()
async def my_wait_readable():
    if name is None:
        name = trio.hazmat.current_task().name
    while True:
        try:
            log.debug('%s: Waiting for fd to become readable...', name)
            await trio.hazmat.wait_readable(fd)
        except trio.ResourceBusyError:
            log.debug('%s: Resource busy, parking in read queue.', name)
            await read_queue.park()
            continue
        log.debug('%s: fd readable, unparking next task.', name)
        read_queue.unpark()
        break

但是,在测试中,我会收到类似以下的消息:

2018-09-18 13:09:17.219 pyfuse3-worker-37: Waiting for fd to become readable...
2018-09-18 13:09:17.219 pyfuse3-worker-47: Waiting for fd to become readable...
2018-09-18 13:09:17.220 pyfuse3-worker-53: Waiting for fd to become readable...
2018-09-18 13:09:17.220 pyfuse3-worker-51: fd readable, unparking next task.
2018-09-18 13:09:17.220 pyfuse3-worker-51: doing work
2018-09-18 13:09:17.221 pyfuse3-worker-47: Resource busy, parking in read queue.
2018-09-18 13:09:17.221 pyfuse3-worker-37: Resource busy, parking in read queue.
2018-09-18 13:09:17.221 pyfuse3-worker-53: Resource busy, parking in read queue.

换句话说:

  1. 所有任务都输入trio.hazmat.wait_readable
  2. 一个任务成功返回并尝试取消停放下一个任务(但没有)
  3. 其他任务收到BusyError并自行停放
  4. 什么都没有发生,因为所有工人都停了车

解决此问题的正确方法是什么?

1 个答案:

答案 0 :(得分:3)

来自同一fd的多个读者没有任何意义,使用Trio(或不使用)不会改变这一基本事实。您为什么首先尝试这样做?

如果由于某些原因您确实确实需要并行执行多个任务来对数据进行后处理,请使用一个读取任务将数据添加到队列中,然后让您的处理任务从该队列中获取数据。

或者,您可以使用锁:

read_lock = trio.Lock()
async def work_loop(fd):
   while True:
     async with read_lock:
        await trio.hazmat.wait_readable(fd)
        buf = os.read(fd, BUFSIZE)
     if not buf:
         break
     await do_work(buf)