Dans les laboratoires précédents, le concept process était présenté, il s'agissait de la principale unité d'allocation de ressources pour les utilisateurs. Dans ce laboratoire, le concept de thread (ou thread ) est présenté, il s'agit de l'unité de planification élémentaire d'un système. En plus des processus, les threads d'exécution constituent un mécanisme par lequel un ordinateur peut exécuter plusieurs tâches simultanément.
Un thread d'exécution existe dans un processus et c'est une unité plus fine qu'elle ne l'est. Lorsqu'un processus est créé, il ne contient qu'un seul thread qui exécute le programme séquentiel. Ce fil peut à son tour créer d'autres threads; ces threads exécuteront des portions du binaire associé au processus en cours, éventuellement identiques au thread d'origine (qui les a créées).
/ dev / sda1
). (E) IP
)Les processus sont utilisés par le responsable de sécurité pour regrouper et allouer des ressources, et les threads pour planifier l'exécution du code qui accède à ces ressources (partagées).
Étant donné que tous les threads d'un processus utilisent l'espace d'adressage du processus auquel ils appartiennent, leur utilisation présente de nombreux avantages:
Les threads d'exécution peuvent être utiles dans de nombreuses situations, par exemple pour améliorer le temps de réponse des applications à interface graphique, où un traitement intensif du processeur est généralement effectué dans un thread différent de celui affichant l'interface. .
Ils simplifient également la structure d'un programme et permettent d'utiliser moins de ressources (car différentes formes d'IPC ne sont pas nécessaires pour communiquer).
Du point de vue de la mise en œuvre, il existe 3 catégories de threads:
En ce qui concerne les threads, POSIX ne spécifie pas s'ils doivent être implémentés dans l'espace utilisateur ou dans l'espace noyau. Linux les implémente dans l'espace noyau, mais ne différencie pas les threads de processus, sauf que les threads partagent l'espace d'adressage (les threads d'exécution et les processus constituent un cas particulier de “tâche”). Pour utiliser les threads en Python, nous devons inclure le module threading.
Le module threading
expose la classe Thread
. De cette maniere, un fil d'execution est créé lorsqu'on initialise la classe:
import threading threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
None
Thread-N
, ou N est un numérotarget
;
Le nouveau fil créé peut etre lancé en exécution en appelant la fonction start(). Il va exécuter le code spécifié par la fonction target
a laquelle on va passer les arguments de args
ou kwargs
.
Pour déterminer le fil d'exécution courant, on peut utiliser la fonction current_thread:
import threading threading.current_thread()
Les threads d'exécution sont attendus à l'aide de la fonction pthread_join:
t = threading.Thread() t.join(timeout=None)
Une fois qu'un thread a exécuté join sur un autre, celui-ci sera bloqué jusqu'a ce que le thread sur lequel on a fait join va terminer son exécution. Si le thread sur lequel on a fait join lancera une excéption du type excepthook sera lancée dans le thread qui a fait join.
join
dans le programme principal pour les threads qu'elle génere.
Un thread de type daemon a comme but de processer certaines opérations sans avoir un impact sur le fil d'exécution principal.
La propriété principale de ces threads est que le programme principal va finir son exécution s'il ne contient que des threads de ce type.
join
sur un thread de type daemon.
Un thread finit son exécution automatiquement, a la fin du code du fil d'exécution.
Pour changer des informations entre le programme principal et d'autres threads, on va utiliser le module queue. A l'aide de ce module, on peut implémenter une queue ou on va placer les messages provenus de la part des fils d'exécution.
Le module supporte la création de 6 types différentes de queues:
queue
, vous pouvez lire la documentation.
Pour créer un queue, on doit instancier l'une des classes du module queue
:
import queue fifo_queue = queue.Queue() lifo_queue = queue.LifoQueue() priority_queue = queue.PriorityQueue()
Les fonctions put et put_nowait ajoutent des éléments dans la queue:
q.put (item, block=True, timeout=None) q.put_nowait (item)
Les fonctions get et get_nowait sortent des éléments de la queue:
q.get (block=True, timeout=None) q.get_nowait ()
Si la queue est vide a l'appel de l'une de ces 2 fonctions, on va lancer une exception de type Empty
On va créer un programme qui génere un thread qui recoit des valeurs du programme principal et qu'y répond:
import queue import threading def worker (): while True: item = q1.get() if item == 'Hello': print (item) q2.put ('SDE') elif item == 'Good': print (item) q2.put ('bye') else: break q1 = queue.Queue() q2 = queue.Queue() t = threading.Thread (target=worker) t.start () q1.put ('Hello') print (q2.get ()) q1.put ('Good') print (q2.get ()) q1.put ('exit') t.join()
Créez un programme qui recoit 3 numeros comme parametres de la ligne de commande et qui lance en exécution 3 threads. Chaque thread recevra comme parametre du programme principal l'un des 3 numéros et qui affiche sur l'écran pair
our impair
, en fonction de la parité du numéro.
Dans le fichier tu directoire 3-numbers, démarrez un fils d'exécution qui affiche les numéros de 0 a 5 à l'intervalle d'une seconde.
join
.
Observez le moment ou le processus finit son exécution. Modifiez le programme principal pour que le thread finisse don exécution immédiatement apres avoir affiché les 2 lignes
main 1 main 2
Dans le fichier du directoire 4-alive, lancez en exécution un thread pour chaque fonction worker définie et attendes seulement les threads toujours en execution (hint: is_alive).
Créez 2 fils d'exécution, l'un qui va générer les numéros primes de 0 a 50 et l'autre qui va générer les numéros carrés parfaits de 0 a 50. Le programme principal va afficher les numéros générées par les 2 threads et il va s'arrêter.
Créez un programme qui va recevoir des commande bash du clavier et qui va les exécuter dans un thread.
Créez un programme qui utilise 3 threads pour trouver le maximum dans une chaîne de longueur 100 000. Chaque thread lit les valeurs de chaîne dans une queue et place le maximum dans une autre. Utilisez la commande queue.join pour la synchronisation.