2016-05-04 4 views
2

asyncio \ queues.pyНеверно за исключением asyncio.Queue.put?

@coroutine 
def put(self, item): 
    """Put an item into the queue. 

    Put an item into the queue. If the queue is full, wait until a free 
    slot is available before adding item. 

    This method is a coroutine. 
    """ 
    while self.full(): 
     putter = futures.Future(loop=self._loop) 
     self._putters.append(putter) 
     try: 
      yield from putter 
     except: 
      putter.cancel() # Just in case putter is not done yet. 
      if not self.full() and not putter.cancelled(): 
       # We were woken up by get_nowait(), but can't take 
       # the call. Wake up the next in line. 
       self._wakeup_next(self._putters) 
      raise 
    return self.put_nowait(item) 

На мой взгляд, putter может быть сделано cancel, set_exception или set_result. get_nowait использование set_result. только cancel и set_exception будут выбрасывать исключение, тогда может произойти except:. Думаю, except: не нужен.

Почему он добавляет except: в Wake up the next in line?

Update: @Vincent _wakeup_next вызов set_result. set_result выполнит self._state = _FINISHED. task1.cancel() будет self._fut_waiter.cancel(), которые возвращаются False. Итак, task1 будет не отменен.

@Vincent благодаря очень

основной причиной является task.cancel может отменить задачу, хотя в будущем, которое ждет задача была set_result (self._state = _FINISHED).

ответ

4

Если задание в ожидании putter отменено, yield from putter поднимает CancelledError. Это может произойти после вызова get_nowait(), и вы хотите удостовериться, что другие маршрутизаторы уведомлены о том, что в очереди доступен новый слот.

Вот пример:

async def main(): 
    # Create a full queue 
    queue = asyncio.Queue(1) 
    await queue.put('A') 
    # Schedule two putters as tasks 
    task1 = asyncio.ensure_future(queue.put('B')) 
    task2 = asyncio.ensure_future(queue.put('C')) 
    await asyncio.sleep(0) 
    # Make room in the queue, print 'A' 
    print(queue.get_nowait()) 
    # Cancel task 1 before giving the control back to the event loop 
    task1.cancel() 
    # Thankfully, the putter in task 2 has been notified 
    await task2 
    # Print 'C' 
    print(await queue.get()) 

EDIT: Более подробная информация о том, что происходит внутри:

  • queue.get_nowait(): putter.set_result(None) называется; состояние клюшки теперь FINISHED, и task1 проснется, когда элемент управления возвращается в цикл событий.
  • task1.cancel(): task1._fut_waiter уже закончен, поэтому task1._must_cancel установлен в True для того, чтобы поднять CancelledError в следующий раз task1 работает.
  • await task2:
    • управление передается обратно в контур управления, и task1._step() работает. A CancelledError выбрасывается внутри сопрограммы: task1._coro.throw(CancelledError()).
    • queue.put ловит исключение. Поскольку очередь не заполнена и 'B' не будет вставлен, следующая клюшка в очереди должна быть извещена: self._wakeup_next(self._putters).
    • Затем CancelledError повторно поднят и пойман в task1._step(). task1 сейчас фактически отменяет сам (super().cancel()).
+0

_wakeup_next call set_result. set_result выполнит 'self._state = _FINISHED'. task1.cancel() будет «self._fut_waiter.cancel()», который возвращает False. – Vince

+0

@Vince 'task1' и' putter' - это не одно и то же будущее. Путч ('task1._fut_waiter') действительно закончен, но' task1' нет, пока он не будет отменен, а 'task1._must_cancel' будет установлен в' True'. – Vincent

+0

В этом случае task1 ожидает (не запускается), когда task1.cancel, поэтому он просто отменяется перед запуском. get_nowait не пробуждает какой-либо клюшки (кроме задачи 1 не запускается). Возможно, нам нужно создать образец, чтобы отменить запущенную задачу1. – Vince

Смежные вопросы