Files d'attente (*queues*)
**************************

**Code source :** Lib/asyncore.py

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

Les files d'attente *asyncio* sont conçues pour être similaires aux
classes du module "queue". Bien que les files d'attente *asyncio* ne
soient pas compatibles avec les programmes à multiples fils
d'exécution, elles sont conçues pour être utilisées spécifiquement
dans le code *async/await*.

Notez que les méthodes des files d'attente *asyncio* n'ont pas de
paramètre *timeout* ; utilisez la fonction "asyncio.wait_for()" pour
effectuer des opérations de file d'attente avec un délai d'attente.

Voir également la section Exemples ci-dessous.


File d'attente (*queue*)
========================

class asyncio.Queue(maxsize=0)

   File d'attente premier entré, premier sorti (FIFO pour *fist in,
   first out*).

   Si *maxsize* est inférieur ou égal à zéro, la taille de la file
   d'attente est infinie. Si c'est un entier supérieur à "0", alors
   "await put()" se bloque lorsque la file d'attente atteint *maxsize*
   jusqu'à ce qu'un élément soit supprimé par "get()".

   Contrairement à la bibliothèque standard multi-fils "queue", la
   taille de la file d'attente est toujours connue et peut être
   renvoyée en appelant la méthode "qsize()".

   Modifié dans la version 3.10: suppression du paramètre *loop*.

   Cette classe n'est pas compatible avec les fils d'exécution
   multiples.

   maxsize

      Nombre d'éléments autorisés dans la file d'attente.

   empty()

      Renvoie "True" si la file d'attente est vide, "False" sinon.

   full()

      Renvoie "True" s'il y a "maxsize" éléments dans la file
      d'attente.

      Si la file d'attente a été initialisée avec "maxsize=0" (la
      valeur par défaut), alors "full()" ne renvoie jamais "True".

   coroutine get()

      Supprime et renvoie un élément de la file d'attente. Si la file
      d'attente est vide, attend qu'un élément soit disponible.

      Raises "QueueShutDown" if the queue has been shut down and is
      empty, or if the queue has been shut down immediately.

   get_nowait()

      Renvoie un élément s'il y en a un immédiatement disponible,
      sinon lève "QueueEmpty".

   coroutine join()

      Bloque jusqu'à ce que tous les éléments de la file d'attente
      aient été récupérés et traités.

      Le nombre de tâches inachevées augmente chaque fois qu'un
      élément est ajouté à la file. Ce nombre diminue chaque fois
      qu'un fil d'exécution consommateur appelle "task_done()" pour
      indiquer que l'élément a été extrait et que tout le travail à
      effectuer dessus est terminé. Lorsque le nombre de tâches non
      terminées devient nul, "join()" débloque.

   coroutine put(item)

      Met un élément dans la file d'attente. Si la file d'attente est
      pleine, attend qu'un emplacement libre soit disponible avant
      d'ajouter l'élément.

      Raises "QueueShutDown" if the queue has been shut down.

   put_nowait(item)

      Ajoute un élément dans la file d'attente sans bloquer.

      Si aucun emplacement libre n'est immédiatement disponible, lève
      "QueueFull".

   qsize()

      Renvoie le nombre d'éléments dans la file d'attente.

   shutdown(immediate=False)

      Shut down the queue, making "get()" and "put()" raise
      "QueueShutDown".

      By default, "get()" on a shut down queue will only raise once
      the queue is empty. Set *immediate* to true to make "get()"
      raise immediately instead.

      All blocked callers of "put()" and "get()" will be unblocked. If
      *immediate* is true, a task will be marked as done for each
      remaining item in the queue, which may unblock callers of
      "join()".

      Ajouté dans la version 3.13.

   task_done()

      Indique qu'une tâche précédemment mise en file d'attente est
      terminée.

      Utilisé par les consommateurs de file d'attente. Pour chaque
      "get()" utilisé pour récupérer une tâche, un appel ultérieur à
      "task_done()" indique à la file d'attente que le traitement de
      la tâche est terminé.

      Si un "join()" est actuellement bloquant, on reprendra lorsque
      tous les éléments auront été traités (ce qui signifie qu'un
      appel à "task_done()" a été effectué pour chaque élément qui a
      été "put()" dans la file).

      "shutdown(immediate=True)" calls "task_done()" for each
      remaining item in the queue.

      Lève une exception "ValueError" si elle est appelée plus de fois
      qu'il n'y avait d'éléments dans la file.


File avec priorité
==================

class asyncio.PriorityQueue

   Une variante de "Queue" ; récupère les entrées par ordre de
   priorité (la plus basse en premier).

   Les entrées sont généralement des *n*-uplets de la forme
   "(priority_number, data)".


Pile (LIFO)
===========

class asyncio.LifoQueue

   Une variante de "Queue" qui récupère en premier les entrées les
   plus récemment ajoutées (dernier entré, premier sorti).


Exceptions
==========

exception asyncio.QueueEmpty

   Cette exception est levée  lorsque la méthode "get_nowait()" est
   appelée sur une file d'attente vide.

exception asyncio.QueueFull

   Exception levée lorsque la méthode "put_nowait()" est appelée sur
   une file d'attente qui a atteint sa *maxsize*.

exception asyncio.QueueShutDown

   Exception raised when "put()" or "get()" is called on a queue which
   has been shut down.

   Ajouté dans la version 3.13.


Exemples
========

Les files d'attente peuvent être utilisées pour répartir la charge de
travail entre plusieurs tâches simultanées :

   import asyncio
   import random
   import time


   async def worker(name, queue):
       while True:
           # Get a "work item" out of the queue.
           sleep_for = await queue.get()

           # Sleep for the "sleep_for" seconds.
           await asyncio.sleep(sleep_for)

           # Notify the queue that the "work item" has been processed.
           queue.task_done()

           print(f'{name} has slept for {sleep_for:.2f} seconds')


   async def main():
       # Create a queue that we will use to store our "workload".
       queue = asyncio.Queue()

       # Generate random timings and put them into the queue.
       total_sleep_time = 0
       for _ in range(20):
           sleep_for = random.uniform(0.05, 1.0)
           total_sleep_time += sleep_for
           queue.put_nowait(sleep_for)

       # Create three worker tasks to process the queue concurrently.
       tasks = []
       for i in range(3):
           task = asyncio.create_task(worker(f'worker-{i}', queue))
           tasks.append(task)

       # Wait until the queue is fully processed.
       started_at = time.monotonic()
       await queue.join()
       total_slept_for = time.monotonic() - started_at

       # Cancel our worker tasks.
       for task in tasks:
           task.cancel()
       # Wait until all worker tasks are cancelled.
       await asyncio.gather(*tasks, return_exceptions=True)

       print('====')
       print(f'3 workers slept in parallel for {total_slept_for:.2f} seconds')
       print(f'total expected sleep time: {total_sleep_time:.2f} seconds')


   asyncio.run(main())
