Maison > Article > développement back-end > Une introduction au partage de ressources entre les threads et aux mécanismes de verrouillage couramment utilisés dans le multithreading Python
Cet article vous présente une introduction au partage de ressources et aux mécanismes de verrouillage couramment utilisés entre les threads dans le multi-threading Python. J'espère qu'il vous sera utile.
Cet article présentera brièvement le partage de ressources inter-thread et les mécanismes de verrouillage couramment utilisés dans la programmation multi-thread.
Dans la programmation multi-thread, le partage de ressources entre les threads est souvent impliqué. Les méthodes de partage de ressources couramment utilisées sont :
Variables globales (globales)
file d'attente (à partir de la file d'attente d'importation de file d'attente)
Mécanisme de verrouillage du partage de ressources couramment utilisé :
Verrouillage
RLock
Semphore
Condition
L'utilisation de variables globales peut réaliser le partage de ressources entre les threads, mot clé global
démonstration de code :
from threading import Thread, Lock lock = Lock() total = 0 '''如果不使用lock那么,最后得到的数字不一定为0;同时loack不支持连续多次acquire,如果这样做了的后果是死锁!''' def add(): global total global lock for i in range(1000000): lock.acquire() total += 1 lock.release() def sub(): global total global lock for i in range(1000000): lock.acquire() total -= 1 lock.release() thread1 = Thread(target=add) thread2 = Thread(target=sub) # 将Thread1和2设置为守护线程,主线程完成时,子线程也一起结束 # thread1.setDaemon(True) # thread1.setDaemon(True) # 启动线程 thread1.start() thread2.start() # 阻塞,等待线程1和2完成,如果不使用join,那么主线程完成后,子线程也会自动关闭。 thread1.join() thread2.join() total
Utilisez la file d'attente pour partager des ressources, la file d'attente est thread-safe.
from threading import Thread, Lock from queue import Queue def add(q): if q.not_full: q.put(1) def sub(q): if q.not_empty: recv = q.get() print(recv) q.task_done() if __name__ =='__main__': # 设置q最多接收3个任务,Queue是线程安全的,所以不需要Lock qu = Queue(3) thread1 = Thread(target=add, args=(qu,)) thread2 = Thread(target=sub, args=(qu,)) thread1.start() thread2.start() # q队列堵塞,等待所有任务都被处理完。 qu.join()
Verrouillage
Lock ne peut pas acquérir de verrouillage en continu, sinon il se retrouvera dans une impasse. La concurrence pour les ressources de verrouillage peut conduire à une impasse.
Le verrouillage réduira les performances.
from threading import Thread, Lock lock = Lock() total = 0 '''如果不使用lock那么,最后得到的数字不一定为0;同时lock不支持连续多次acquire,如果这样做了的后果是死锁!''' def add(): global total global lock for i in range(1000000): lock.acquire() total += 1 lock.release() def sub(): global total global lock for i in range(1000000): lock.acquire() total -= 1 lock.release() thread1 = Thread(target=add) thread2 = Thread(target=sub) # 将Thread1和2设置为守护线程,主线程完成时,子线程也一起结束 # thread1.setDaemon(True) # thread1.setDaemon(True) # 启动线程 thread1.start() thread2.start() # 阻塞,等待线程1和2完成,如果不使用join,那么主线程完成后,子线程也会自动关闭。 thread1.join() thread2.join() total
RLock
RLock peut acquérir des verrous en continu, mais un nombre correspondant de versions est nécessaire pour libérer les verrous
Parce qu'il peut acquérir des verrous en continu, la fonction appelle donc la fonction verrouillée à l'intérieur de la fonction
from threading import Thread, Lock, RLock lock = RLock() total = 0 def add(): global lock global total # RLock实现连续获取锁,但是需要相应数量的release来释放资源 for i in range(1000000): lock.acquire() lock.acquire() total += 1 lock.release() lock.release() def sub(): global lock global total for i in range(1000000): lock.acquire() total -= 1 lock.release() thread1 = Thread(target=add) thread2 = Thread(target=sub) thread1.start() thread2.start() thread1.join() thread2.join() total
Variable de condition de condition
Condition La variable de condition obéit au protocole de gestion du contexte : utilisez l'instruction with pour acquérir un verrou associé pour la durée du bloc englobant.
La méthode wait() libère le verrou puis se bloque jusqu'à ce qu'un autre thread le réveille en appelant notify() ou notify_all(). Une fois réveillé, wait() réacquiert le verrou et revient. Un délai d'attente peut également être spécifié.
Démarrez d'abord la fonction d'attente pour recevoir le signal, qui est dans un état d'attente bloquant, puis démarrez la fonction de notification pour envoyer le signal
from threading import Thread, Condition '''聊天 Peaple1 : How are you? Peaple2 : I`m fine, thank you! Peaple1 : What`s your job? Peaple2 : My job is teacher. ''' def Peaple1(condition): with condition: print('Peaple1 : ', 'How are you?') condition.notify() condition.wait() print('Peaple1 : ', 'What`s your job?') condition.notify() condition.wait() def Peaple2(condition): with condition: condition.wait() print('Peaple2 : ', 'I`m fine, thank you!') condition.notify() condition.wait() print('Peaple2 : ', 'My job is teacher.') condition.notify() if __name__ == '__main__': cond = Condition() thread1 = Thread(target=Peaple1, args=(cond,)) thread2 = Thread(target=Peaple2, args=(cond,)) # 此处thread2要比thread1提前启动,因为notify必须要有wait接收;如果先启动thread1,没有wait接收notify信号,那么将会死锁。 thread2.start() thread1.start() # thread1.join() # thread2.join()
Semphore
Cette classe implémente l'objet sémaphore. Le sémaphore gère un compteur atomique qui représente le nombre d'appels release() moins le nombre d'appels acquire() plus une valeur initiale. Si nécessaire, la méthode acquire() se bloque jusqu'à ce qu'elle puisse revenir sans rendre le compteur négatif. Si elle n’est pas indiquée, la valeur par défaut est 1.
#Semaphore 是用于控制进入数量的锁 #文件, 读、写, 写一般只是用于一个线程写,读可以允许有多个 import threading import time class HtmlSpider(threading.Thread): def __init__(self, url, sem): super().__init__() self.url = url self.sem = sem def run(self): time.sleep(2) print("Download {html} success\n".format(html=self.url)) self.sem.release() class UrlProducer(threading.Thread): def __init__(self, sem): super().__init__() self.sem = sem def run(self): for i in range(20): self.sem.acquire() html_thread = HtmlSpider("https://www.baidu.com/{}".format(i), self.sem) html_thread.start() if __name__ == "__main__": # 控制锁的数量, 每次同时会有3个线程获得锁,然后输出 sem = threading.Semaphore(3) url_producer = UrlProducer(sem) url_producer.start()
Dans la programmation multi-processus, les variables globales ne peuvent pas être partagées entre les processus et la file d'attente.Queue
ne peut pas être utilisé.La communication par programmation multi-processus nécessite l'utilisation de Queue, Pipe
Si vous utilisez la programmation de processus par pool de processus, vous en avez besoin. d'utiliser la file d'attente de l'instance Manger pour réaliser la communication
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!