2016-08-10 9 views
0

Я пытался создать версию питона асинхронной из в Java CountDownLatchПочему condition.notify_all пробуждает только одного официанта?

class CountDownLatch: 
    def __init__(self, count=1): 
     if count == 0: 
      raise ValueError('count should be more than zero') 
     self.count = count 
     self.countdown_over = aio.Condition() 

    async def countdown(self): 
     with await self.countdown_over: 
      print('decrementing counter') 
      self.count -= 1 
      print('count {}'.format(self.count)) 
      if self.count == 0: 
       print('count is zero no more waiting') 
       await aio.sleep(1) 
       self.countdown_over.notify_all() 

    async def wait(self): 
     with await self.countdown_over: 
      await self.countdown_over.wait() 

Теперь я пытаюсь его.

In [2]: async def g(latch): 
    ...:  await latch.wait() 
    ...:  print('g') 
    ...: 

In [3]: async def f(latch): 
    ...:  print('counting down') 
    ...:  await latch.countdown() 
    ...:  await g(latch) 
    ...: 

In [4]: def run(): 
    ...:  latch = CountDownLatch(2) 
    ...:  loop = aio.get_event_loop() 
    ...:  loop.run_until_complete(aio.wait((f(latch), f(latch)))) 
    ...: 

In [5]: import asyncio as aio 

In [6]: from new.tests.test_turnovers import CountDownLatch 

А вот выходного

counting down 
decrementing counter 
count 1 
counting down 
decrementing counter 
count 0 
count is zero no more waiting 
g 

Я не могу понять, что я делаю неправильно здесь. Счетчик создается и уменьшается просто отлично. Одна сопрограмма даже уведомляется и выполняет свою задачу, но вторая не по какой-то причине.

ответ

1

Позвольте f1 быть f названным первым, и пусть f2 будет f называется вторым. Следует отметить, что даже если вы использовали async ключевое слово f функция синхронно пока не надеты latch.wait(). Поэтому мы можем легко отладить то, что происходит:

  1. f1 пожары.
  2. count уменьшается на 1
  3. f1 входит await self.countdown_over.wait() и переключение контекста происходит
  4. f2 пожаров
  5. count уменьшается на 1, f2 входит if условию
  6. self.countdown_over.notify_all() пожаров. Все официанты уведомляются (обратите внимание, что в этот момент это только f1).
  7. f2 входит await self.countdown_over.wait() и контекст переключения происходит
  8. f1 просыпается и покидает .wait() позвонить

Обратите внимание, что шаг 7 происходит после шаг 6. Таким образом f2 никогда не уведомила.

Как правило, если у вас несколько (зеленых или нет) потоков, которые уведомляют и ждут (в этом порядке, синхронно), то по крайней мере один из них всегда будет работать.