Maison >développement back-end >Tutoriel Python >Une introduction au partage de ressources entre les threads et aux mécanismes de verrouillage couramment utilisés dans le multithreading Python

Une introduction au partage de ressources entre les threads et aux mécanismes de verrouillage couramment utilisés dans le multithreading Python

不言
不言avant
2018-10-26 17:18:592905parcourir

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

( 1) Partage de ressources entre les threads

  1. 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
  1. 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()

(2) Verrouillage (Verrouillage/RLock/Condition/Semphore)

  1. 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
  1. 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
  1. 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()
  1. 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()

(3) Une brève introduction à la programmation multi-processus

  1. 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é.
  2. La communication par programmation multi-processus nécessite l'utilisation de Queue, Pipe

  3. 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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer