Contenu | Rechercher | Menus

Annonce

DVD, clés USB et t-shirts Ubuntu-fr disponibles sur la boutique En Vente Libre

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 22/11/2020, à 15:22

DonutMan75

[RESOLU] [Pyhton] Question noob concernant les threads

Bonjour à tous,
je me mets doucement à Pyhton, notamment grâce au livre "Introducing Python" de Bill Lubanovic aux éditions O'reilly.

Ce dernier présente l'utilisation des threads sous python via le module threading. C'est une présentation assez sommaire et j'ai voulu la tester un peu, notamment au regard du fonctionnement des pthreads en C.

Mon objectif est donc de créer deux threads sous Python qui vont chacun lire et incrémenter une variable commune n. Le programme sera écrit de manière à ce que les deux threads se marchent volontairement sur les pieds, mettant ainsi en évidence la nécessité d'un mécanisme de lock. (c'est un exemple adapté du livre "Développement système sous Linux" de Christophe Blaess aux éditions Eyrolles).

Mon code :

import threading as th
from time import sleep

n = 0

def traitement(k):
    if k==1: # premier thread
        wait_time = 2
    else:
        wait_time = 1
    
    print("- [THREAD %d] je lis la valeur n=%d, je vais l'incrémenter de 1..." %(k, n))
    sleep(wait_time)
    n=n+1
    print('- [THREAD %d] done ! désormais n = %d !' %(k, n))
    

# Création de deux threads
for k in range(2): # [0, 1]
    d_thread = th.Thread(target=traitement, args=(k+1,))
    print('- [PERE] Lancement du thread %d...' % (k+1))
    d_thread.start()

Hélas ça plante, j'obtiens :

- [PERE] Lancement du thread 1...
- [PERE] Lancement du thread 2...
Exception in thread Thread-53:
Traceback (most recent call last):
  File "/home/donut/anaconda3/envs/donut/lib/python3.8/threading.py", line 932, in _bootstrap_inner
Exception in thread     Thread-54self.run()
  File "/home/donut/anaconda3/envs/donut/lib/python3.8/threading.py", line 870, in run
:
Traceback (most recent call last):
  File "/home/donut/anaconda3/envs/donut/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self._target(*self._args, **self._kwargs)
  File "/home/donut/python/test/test_chap11.py", line 85, in traitement
    print("- [THREAD %d] je lis la valeur n=%d, je vais l'incrémenter de 1..." %(k, n))
UnboundLocalError: local variable 'n' referenced before assignment
    self.run()
  File "/home/donut/anaconda3/envs/donut/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/home/donut/python/test/test_chap11.py", line 85, in traitement
    print("- [THREAD %d] je lis la valeur n=%d, je vais l'incrémenter de 1..." %(k, n))
UnboundLocalError: local variable 'n' referenced before assignment

Je comprends que les threads n'ont pas accès à la variable n, qui est pourtant définie au tout début du main...
C'est un comportement étrange puisque, par exemple, l'appel à la fonction print_a() ci-dessous fonctionnera correctement :

a = 10

def print_a():
    print("a = %d" % a)
    
print_a()

Je m'en sors en rajoutant un "global n" dans la définition de la fonction traitement()

def traitement(k):
    global n
    if k==1: # premier thread
        wait_time = 2
    else:
        wait_time = 1
    
    print("- [THREAD %d] je lis la valeur n=%d, je vais l'incrémenter de 1..." %(k, n))
    sleep(wait_time)
    n=n+1
    print('- [THREAD %d] done ! désormais n = %d !' %(k, n))

Néanmoins, je ne comprends pas bien... il me semblait que les threads manipulaient exactement les mêmes variables que celles visibles lors de leur lancement... j'ai loupé quelque chose ?

Merci d'avance smile

D.

Dernière modification par DonutMan75 (Le 22/11/2020, à 16:16)

Hors ligne

#2 Le 22/11/2020, à 15:33

pseudofab

Re : [RESOLU] [Pyhton] Question noob concernant les threads

Bonjour,
c'est parce que tu modifies la variable que tu as besoin de global .

>>> def print_a():
...     a += 1
...     print(a)
... 
>>> print_a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in print_a
UnboundLocalError: local variable 'a' referenced before assignment
>>> 

Dernière modification par pseudofab (Le 22/11/2020, à 15:34)


# Là pour le plaisir de découvrir ...
# Envie d'apprendre Bash ou Python? Pensez aux MOOC ---> https://forum.ubuntu-fr.org/viewtopic.php?id=2030646

Hors ligne

#3 Le 22/11/2020, à 16:16

DonutMan75

Re : [RESOLU] [Pyhton] Question noob concernant les threads

Hello,
super merci pour cette réponse ! Je dois encore consolider mes bases ^^
Donc le code complété avec global ça semble propre ? Ou bien y'a-t-il une meilleure façon de faire ?
Bon enfin de toute façon, pour creuser vraiment le mieux serait que je lise la doc du module.
Merci en tout cas pour ton coup de main smile

D.

Dernière modification par DonutMan75 (Le 22/11/2020, à 16:18)

Hors ligne

#4 Le 23/11/2020, à 13:19

pseudofab

Re : [RESOLU] [Pyhton] Question noob concernant les threads

DonutMan75 a écrit :

Donc le code complété avec global ça semble propre ? Ou bien y'a-t-il une meilleure façon de faire ?

Bonjour,
On évite les globals dans la mesure où ça devient vite confus dans les espaces de nommage. à proscrire sur un code conséquent.
Autant prendre les bonnes habitudes dès maintenant en utilisant une classe, voir un dico,une liste .. ou un return avec la variable modifiée

>>> class Truc:
...     def __init__(self):
...             self.a = 0
...     def ajoute1(self):
...             self.a += 1
...             print(self.a)
... 
>>> t = Truc()
>>> t.ajoute1()
1
>>> 
>>> d = dict()
>>> d['a'] = 0
>>> def truc():
...     d['a'] += 1 
... 
>>> truc()
>>> print(d['a'])
1
>>> 
>>> a = 0
>>> def truc(arg):
...     arg += 1
...     return arg
... 
>>> a = truc(a)
>>> print(a)
1

Dernière modification par pseudofab (Le 23/11/2020, à 13:20)


# Là pour le plaisir de découvrir ...
# Envie d'apprendre Bash ou Python? Pensez aux MOOC ---> https://forum.ubuntu-fr.org/viewtopic.php?id=2030646

Hors ligne