Filas
*****

**Código-fonte:** Lib/asyncio/queues.py

======================================================================

Filas asyncio são projetadas para serem similar a classes do módulo
"queue". Apesar de filas asyncio não serem seguras para thread, elas
são projetadas para serem usadas especificamente em código
async/await.

Perceba que métodos de filas asyncio não possuem um parâmetro
*timeout*; use a função "asyncio.wait_for()" para realizar operações
de fila com um tempo limite timeout.

Veja também a seção Exemplos abaixo.


Queue
=====

class asyncio.Queue(maxsize=0)

   Uma fila onde o primeiro a entrar, é o primeiro a sair (FIFO -
   First In First Out).

   Se *maxsize* for menor que ou igual a zero, o tamanho da fila é
   infinito. Se ele for um inteiro maior que "0", então "await put()"
   bloqueia quando a fila atingir *maxsize* até que um item seja
   removido por "get()".

   Ao contrário da biblioteca padrão de threading "queue", o tamanho
   da fila é sempre conhecido e pode ser obtido através da chamada do
   método "qsize()".

   Alterado na versão 3.10: Removido o parâmetro *loop*.

   Esta classe não é segura para thread.

   maxsize

      Número de itens permitidos na fila.

   empty()

      Retorna "True" se a fila estiver vazia, "False" caso contrário.

   full()

      Retorna "True" se existem "maxsize" itens na fila.

      Se a fila foi inicializada com "maxsize=0" (o padrão), então
      "full()" nunca retorna "True".

   async get()

      Remove e retorna um item da fila. Se a fila estiver vazia,
      aguarda até que um item esteja disponível.

   get_nowait()

      Retorna um item se houver um imediatamente disponível, caso
      contrário levanta "QueueEmpty".

   async join()

      Bloqueia até que todos os itens na fila tenham sido recebidos e
      processados.

      A contagem de tarefas inacabadas aumenta sempre que um item é
      adicionado à fila. A contagem diminui sempre que uma corrotina
      consumidora chama "task_done()" para indicar que o item foi
      recuperado e todo o trabalho nele foi concluído. Quando a
      contagem de tarefas inacabadas chega a zero, "join()"
      desbloqueia.

   async put(item)

      Coloca um item na fila. Se a fila estiver cheia, aguarda até que
      uma posição livre esteja disponível antes de adicionar o item.

   put_nowait(item)

      Coloca um item na fila sem bloqueá-la.

      Se nenhuma posição livre estiver imediatamente disponível,
      levanta "QueueFull".

   qsize()

      Retorna o número de itens na fila.

   task_done()

      Indica que o item de trabalho anteriormente enfileirado está
      concluído.

      Usada por consumidores de fila. Para cada "get()" usado para
      buscar um item de trabalho, uma chamada subsequente para
      "task_done()" avisa à fila, que o processamento no item de
      trabalho está concluído.

      Se um "join()" estiver sendo bloqueado no momento, ele irá
      continuar quando todos os itens tiverem sido processados
      (significando que uma chamada "task_done()" foi recebida para
      cada item que foi chamado o método "put()" para colocar na
      fila).

      Levanta "ValueError" se chamada mais vezes do que a quantidade
      de itens existentes na fila.


Fila de prioridade
==================

class asyncio.PriorityQueue

   Uma variante de "Queue"; recupera entradas em ordem de prioridade
   (mais baixas primeiro).

   Entradas são tipicamente tuplas no formato "(priority_number,
   data)".


Filas LIFO (último a entrar, primeiro a sair)
=============================================

class asyncio.LifoQueue

   Uma variante de "Queue" que recupera as entradas adicionadas mais
   recentemente primeiro (último a entrar, primeiro a sair).


Exceções
========

exception asyncio.QueueEmpty

   Esta exceção é levantada quando o método "get_nowait()" é chamado
   em uma fila vazia.

exception asyncio.QueueFull

   Exceção levantada quando o método "put_nowait()" é chamado em uma
   fila que atingiu seu *maxsize*.


Exemplos
========

Filas podem ser usadas para distribuir cargas de trabalho entre
diversas tarefas concorrentes:

   import asyncio
   import random
   import time


   async def worker(nome, fila):
       while True:
           # Retira um "item de trabalho" da fila.
           dormir_por = await queue.get()

           # Dorme por "dormir_por" segundos.
           await asyncio.sleep(dormir_por)

           # Notifica a fila de que o "item de trabalho" foi processado.
           fila.task_done()

           print(f'{nome} dormiu por {dormir_por:.2f} segundos')


   async def main():
       # Cria uma fila para armazenar nossa "carga de trabalho".
       fila = asyncio.Queue()

       # Gera tempos aleatórios e os insere na fila.
       tempo_total_de_sono = 0
       for _ in range(20):
           dormir_por = random.uniform(0.05, 1.0)
           tempo_total_de_sono += dormir_por
           fila.put_nowait(dormir_por)

       # Cria três tarefas "worker" para processarem a fila concorrentemente.
       tarefas = []
       for i in range(3):
           tarefa = asyncio.create_task(worker(f'worker-{i}', fila))
           tarefas.append(tarefa)

       # Espera até a fila ser completamente processada.
       comecou_em = time.monotonic()
       await fila.join()
       tempo_dormido = time.monotonic() - comecou_em

       # Cancela nossas tarefas.
       for tarefa in tarefas:
           tarefa.cancel()
       # Espera até todas as tarefas serem canceladas.
       await asyncio.gather(*tarefas, return_exceptions=True)

       print('====')
       print(f'3 trabalhadoras dormiram em paralelo por {tempo_dormido:.2f} segundos')
       print(f'Tempo total de sono esperado: {tempo_total_de_sono:.2f} segundos')


   asyncio.run(main())
