Maison  >  Article  >  Comment éviter une impasse ?

Comment éviter une impasse ?

coldplay.xixi
coldplay.xixioriginal
2020-06-24 14:33:244453parcourir

Comment éviter une impasse ?

Méthodes pour éviter les blocages :

Un blocage se produit lorsque deux threads s'attendent pour libérer des ressources. L'interpréteur Python ne surveille pas et ne prend pas activement de mesures pour gérer les situations de blocage. Des mesures doivent donc être prises pour éviter les blocages lors de l'exécution d'une programmation multithread.

Une fois qu'un blocage se produit, aucune exception ne se produira dans l'ensemble du programme, et aucune invite ne sera donnée, mais tous les threads seront bloqués et incapables de continuer.

Un blocage est très facile à se produire, surtout lorsqu'il y a plusieurs moniteurs de synchronisation dans le système. Le programme suivant provoquera un blocage :

import threading
import time
class A:
    def __init__(self):
        self.lock = threading.RLock()
    def foo(self, b):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name\
                + " 进入了A实例的foo()方法" )     # ①
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name\
                + " 企图调用B实例的last()方法")   # ③
            b.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
            print("进入了A类的last()方法内部")
        finally:
            self.lock.release()
class B:
    def __init__(self):
        self.lock = threading.RLock()
    def bar(self, a):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name\
                + " 进入了B实例的bar()方法" )   # ②
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name\
                + " 企图调用A实例的last()方法")  # ④
            a.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
            print("进入了B类的last()方法内部")
        finally:
            self.lock.release()
a = A()
b = B()
def init():
    threading.current_thread().name = "主线程"
    # 调用a对象的foo()方法
    a.foo(b)
    print("进入了主线程之后")
def action():
    threading.current_thread().name = "副线程"
    # 调用b对象的bar()方法
    b.bar(a)
    print("进入了副线程之后")
# 以action为target启动新线程
threading.Thread(target=action).start()
# 调用init()函数
init()

Exécutez le programme ci-dessus et il verra l'effet affiché. dans la figure 1.

Comment éviter une impasse ?

Figure 1 Effet de blocage

Comme le montre la figure 1, le programme ne peut ni s'exécuter vers le bas ni lancer d'anomalie, il a été "impasse". La raison en est que les méthodes de l'objet A et de l'objet B dans le programme ci-dessus sont toutes deux des méthodes thread-safe.

Il y a deux threads qui s'exécutent dans le programme. Le corps d'exécution du thread secondaire est la fonction action(), et le corps d'exécution du thread principal est la fonction init() (le programme principal appelle la fonction init()). Dans la fonction action(), laissez l'objet B appeler la méthode bar(), et dans la fonction init(), laissez l'objet A appeler la méthode foo().

La figure 1 montre que la fonction action() est exécutée en premier et appelle la méthode bar() de l'objet B. Avant d'entrer dans la méthode bar(), le thread verrouille le Lock de l'objet B (lorsque le le programme est exécuté en code ②, le thread secondaire fait une pause pendant 0,2 s) ; le CPU passe à l'exécution d'un autre thread et laisse l'objet A exécuter la méthode foo(), vous voyez donc que le thread principal commence à exécuter la méthode foo(). de l'instance A. Avant d'entrer dans la méthode foo(), ce thread verrouille le verrou de l'objet A (lorsque le programme exécute le code ①, le thread principal fait également une pause pendant 0,2 s).

Ensuite, le thread secondaire se réveillera en premier et continuera à s'exécuter vers le bas jusqu'à ce qu'il atteigne le code ④ et espère appeler la méthode last() de l'objet A (avant d'exécuter cette méthode, le Lock de l'objet A doit être le premier Lock), mais à ce moment-là, le thread principal maintient le verrou de l'objet A, donc le thread secondaire est bloqué.

Le thread principal devrait ensuite se réveiller et continuer à s'exécuter jusqu'à ce qu'il atteigne le code ③ où il espère appeler la méthode last() de l'objet B (avant d'exécuter cette méthode, l'objet B doit d'abord être verrouillé) , mais à ce moment, le thread secondaire ne libère pas le verrou de l'objet B.

À ce stade, le thread principal détient le verrou sur l'objet A et attend que l'objet B soit verrouillé, tandis que le thread secondaire détient le verrou sur l'objet B et attend que l'objet A soit verrouillé. attendez-vous l'un l'autre. Le verrou est libéré en premier, donc une impasse se produit.

Les blocages ne doivent pas se produire dans les programmes. Les blocages doivent être évités autant que possible lors de l'écriture de programmes. Il existe plusieurs façons courantes de résoudre les problèmes de blocage :

  1. Évitez plusieurs verrous. Essayez d'éviter de verrouiller plusieurs verrous sur le même thread. Par exemple, dans le programme de blocage ci-dessus, le thread principal doit verrouiller le verrou de deux objets A et B, et le thread secondaire doit également verrouiller le verrou de deux objets A et B, ce qui présente un danger caché de blocage.

  2. a la même séquence de verrouillage. Si plusieurs threads doivent verrouiller plusieurs verrous, ils doivent s’assurer qu’ils demandent les verrous dans le même ordre. Par exemple, dans le programme de blocage ci-dessus, le thread principal verrouille d'abord le verrou de l'objet A, puis verrouille le verrou de l'objet B, tandis que le thread secondaire verrouille d'abord le verrou de l'objet B, puis verrouille le verrou de l'objet A. Cette séquence de verrouillage peut facilement former des verrous imbriqués, ce qui peut conduire à des blocages. Ce problème peut être évité si le thread principal et le thread secondaire se bloquent dans le même ordre.

  3. Utilisez le verrouillage horaire. Le programme peut spécifier le paramètre timeout lors de l'appel de la méthode acquire() pour verrouiller. Ce paramètre spécifie que le verrou sera automatiquement libéré après l'expiration du délai d'attente, afin que le blocage puisse être déverrouillé.

  4. Détection de blocage. La détection des interblocages est un mécanisme de prévention des interblocages qui repose sur des mécanismes algorithmiques. Elle est principalement destinée aux scénarios dans lesquels le verrouillage séquentiel est impossible et les verrous temporisés ne peuvent pas être utilisés.

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:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn