2016-05-04 4 views
2

asyncioの\のqueues.pyasyncio.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) 

puttercancelset_exceptionまたはset_resultによって行うことができます。 get_nowaitset_resultを使用してください。 cancelset_exceptionのみが例外をスローすると、except:が発生する可能性があります。私はexcept:は必要ないと思う。

なぜexcept:Wake up the next in lineが追加されますか?

更新: @Vincent _wakeup_nextコールset_resultset_resultself._state = _FINISHEDを実行します。 task1.cancel()self._fut_waiter.cancel()はFalseを返します。したがって、task1はをキャンセルしていないになります。

@Vincentおかげで非常に多くの

キー原因は、タスクが待っている未来はset_resultメソッドしたがtask.cancelは(self._state = _FINISHED)タスクをキャンセルすることができます。

答えて

4

putterを待つタスクがキャンセルされた場合、yield from putterCancelledErrorを発生させます。これは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が実行されたときにはCancelledErrorを発生させるためにtask1._must_cancelTrueに設定されます。
  • await task2
    • 制御は、制御ループ、及びtask1._step()実行に戻って与えられます。コルーチン内にCancelledErrorがスローされます。task1._coro.throw(CancelledError())
    • queue.putは例外をキャッチします。キューがいっぱいではなく、'B'が挿入されないため、キュー内の次のパターに通知する必要があります。self._wakeup_next(self._putters)
    • そして、CancelledErrorが再発生され、task1._step()に捕らえられます。 task1は実際にはそれ自体をキャンセルします(super().cancel())。
+0

_wakeup_next呼び出し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はパターを起動させません(ただし、task1はトリガーされません)。たぶん、実行中のタスクを取り消すサンプルを作成する必要があります1。 – Vince

関連する問題