Nous avons présenté python Plusieurs outils de synchronisation de fil pour. Python Synchronisation des fils(Un.) — Conditions concurrentielles et verrouillage des filspython Synchronisation des fils(2.) — Objet conditionnelpython Synchronisation des fils(Trois) — Sémaphore
Les outils de synchronisation de thread décrits dans cet article sont comparés aux trois types d'outils décrits ci - dessus , Plus simple et plus pratique .
L'utilisation d'événements est l'un des mécanismes les plus simples de communication entre les fils — Un thread signale un événement, Un autre fil attend et répond au signal . python threading Objet de l'événement fourni dans le paquet Event C'est pour ça.. Lorsque le BIT de drapeau dans l'objet de l'événement est défini par True Devient False, Tous les Threads qui attendent sur cet événement seront réveillés . Donc,,python Objet de l'événement dans Event Les méthodes suivantes sont disponibles pour l'appel :
is_set()
Renvoie si le drapeau de l'événement est True.
set()
Définissez le BIT de drapeau interne de l'événement à True, Puis réveillez tous les Threads qui attendent sur cet événement .
clear()
Effacer le drapeau, Réinitialiser le drapeau de l'événement à False, Par la suite, plusieurs Threads peuvent être re - bloqués sur l'objet Event .
wait(timeout=None)
Bloquez le fil jusqu'à ce que la variable interne soit true. Si le drapeau interne est true,Retour immédiat. Sinon, le fil sera bloqué ,Jusqu'à ce que set() La méthode définit le drapeau à true Ou un délai optionnel s'est produit . Si c'est à cause d'un délai de retour ,Renvoie False,Sinon, il reviendra True.
L'exemple suivant montre tous les 5 Les Threads sont bloqués sur un objet d'événement ,Jusqu'à3Dans quelques secondes.,Appel du fil principal set La méthode déclenche le signal de l'événement ,Vous pouvez voir tout 5 Les Threads commencent immédiatement à exécuter .
import logging
from threading import Thread, Event
from time import sleep
class EventThread(Thread):
def __init__(self, event, id):
super().__init__()
self._event = event
self._id = id
def run(self):
logging.info('%r start running' % self)
self._event.wait()
logging.info('%r continue running after event' % self)
def __repr__(self):
return 'EventThread(%s)' % self._id
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')
event = Event()
for i in range(5):
thread = EventThread(event, i)
thread.start()
logging.info('main start sleeping')
sleep(3)
logging.info('main set event')
event.set()
Imprimé:
2019-05-14 09:15:50,626 - INFO: EventThread(0) start running 2019-05-14 09:15:50,626 - INFO: EventThread(1) start running 2019-05-14 09:15:50,626 - INFO: EventThread(2) start running 2019-05-14 09:15:50,626 - INFO: EventThread(3) start running 2019-05-14 09:15:50,626 - INFO: EventThread(4) start running 2019-05-14 09:15:50,626 - INFO: main start sleeping 2019-05-14 09:15:53,639 - INFO: main set event 2019-05-14 09:15:53,645 - INFO: EventThread(1) continue running after event 2019-05-14 09:15:53,645 - INFO: EventThread(0) continue running after event 2019-05-14 09:15:53,645 - INFO: EventThread(2) continue running after event 2019-05-14 09:15:53,645 - INFO: EventThread(4) continue running after event 2019-05-14 09:15:53,645 - INFO: EventThread(3) continue running after event
La classe palissade est un autre primitif synchrone simple , Nous avons déjà présenté Linux Avec Java Clôture en . java Classe d'outils de synchronisation des Threads
L'objet palissade est utilisé pour permettre à plusieurs Threads d'attendre l'un l'autre . Il a maintenu un compteur interne , Les valeurs sont passées par défaut par la méthode de construction , Chaque fois qu'un thread appelle wait Méthodes, La valeur est atomiquement diminuée de 1,Jusqu'à ce qu'il soit réduit à 0, Alors laissez tous les blocages wait Le thread sur cet objet de clôture continue .
Barrier(parties, action=None, timeout=None)
wait(timeout=None)
La méthode la plus importante dans un objet de clôture est wait Voilà.. Blocage du fil en attente,Jusqu'à ce que la méthode de construction passe parties Les fils sont bloqués en attendant wait Méthode ou délai , Si le délai d'entrée de la méthode est None, Utilisez le délai par défaut passé par la méthode de construction . Une fois le délai écoulé , La clôture sera immédiatement endommagée , D'autres fils qui restent bloqués en attendant cette clôture recevront wait Méthode lancée BrokenBarrierError Anomalie. Si vous essayez d'appeler sur un objet de clôture brisé wait Méthodes, Il va aussi lancer immédiatement BrokenBarrierError Anomalie. Renvoie un nombre,La valeur est: 0 À parties - 1, L'interpréteur garantit que tous les fils qui attendent sur la même clôture , Chaque valeur de retour est différente , Pour que vous puissiez compter sur wait Valeur de retour de la méthode pour faire un peu de traitement . Si l'objet palissade est fourni dans le constructeur lors de la création action Paramètres, Il sera appelé avant qu'un des Threads ne soit libéré . Si cet appel soulève une exception , L'objet de la clôture sera brisé .
reset()
Réinitialiser la clôture à l'état initial par défaut . S'il reste des fils à libérer dans la clôture , Ces fils recevront BrokenBarrierError Anomalie. Sauf si c'est très nécessaire, Sinon, cette méthode n'est pas recommandée , Souvent, au lieu de réutiliser une clôture dont l'état est inconnu , Autant en créer un nouveau .
abort()
Mettre la clôture dans un état endommagé . Cela conduit à tous les appels passés et futurs wait() Lancé dans la méthode BrokenBarrierError Anomalie.
L'utilisation de la clôture est simple ,Mais c'est très pratique,Dans l'environnement réel, Nous avons souvent besoin d'appeler simultanément de nombreuses interfaces d'affaires , Et recueillir leurs retours , Ensuite, après le retour de toutes les interfaces, passer à l'étape suivante . Mais tous les appels d'interface ne sont pas nécessaires , Donc pour ce scénario , Une façon nécessaire d'optimiser est une fois que les interfaces nécessaires sont collectées , Interrompre immédiatement les appels vers d'autres interfaces , Et commencer l'opération après ça . Les exigences ci - dessus peuvent sembler très simples et élégantes si vous utilisez des clôtures pour les résoudre ,Bien que Python Nous ne pouvons pas mettre fin au thread en dehors du thread , Mais on peut passer par la clôture abort La méthode permet aux Threads qui n'ont pas encore terminé l'exécution de lancer une exception une fois l'exécution terminée , Pour qu'on n'ait pas besoin de se concentrer sur eux . L'exemple suivant simule le processus décrit ci - dessus .
import logging
import random
from threading import Thread, Barrier
from time import sleep, time
class InterfaceThread(Thread):
def __init__(self, majorbarrier, minorbarrier, id, major):
super().__init__()
self._majorbarrier = majorbarrier
self._minorbarrier = minorbarrier
self._id = id
self._major = major
def run(self):
nsec = random.uniform(0, 4)
logging.info('%r start running sleep %s' % (self, nsec))
sleep(nsec)
logging.info('%r after sleeping' % self)
if self._major:
try:
result = self._majorbarrier.wait()
if result == 0:
self._minorbarrier.abort()
except:
logging.error('%s waitting on majorbarrier aborted' % self)
return
else:
try:
self._minorbarrier.wait()
except:
logging.warning('%s watting on minorbarrier aborted' % self)
return
logging.info('%r continue running after barrier' % self)
def __repr__(self):
return 'InterfaceThread(%s【major: %s】)' % (self._id, self._major)
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s: %(message)s')
start = time()
majorbarrier = Barrier(4)
minorbarrier = Barrier(3)
threads = list()
for i in range(6):
threads.append(InterfaceThread(majorbarrier, minorbarrier, i, bool(i >= 3)))
for thread in threads:
thread.start()
result = majorbarrier.wait()
if result == 0:
minorbarrier.abort()
logging.info('run by %s' % (time() - start))
Deux objets de clôture ont été créés dans l'exemple ci - dessus , Utilisé pour synchroniser les appels d'interface nécessaires et non nécessaires, respectivement ,Nous avons passé au hasard sleep 0 À 4 Secondes pour simuler un appel d'interface . Dès que la clôture sera nécessaire wait Retour de la méthode 0, Cela signifie que toutes les interfaces nécessaires ont été retournées , Pour le moment, vous pouvez appeler la clôture non nécessaire abort Pour détruire une clôture inutile , Pendant ce temps, le programme continue , Cela permet de réduire au minimum le temps de fonctionnement global .
Imprimé:
2019-05-14 14:00:05,045 - INFO: InterfaceThread(0【major: False】) start running sleep 1.3645551759667334 2019-05-14 14:00:05,050 - INFO: InterfaceThread(1【major: False】) start running sleep 3.5451267021153607 2019-05-14 14:00:05,050 - INFO: InterfaceThread(2【major: False】) start running sleep 3.0433784558963644 2019-05-14 14:00:05,052 - INFO: InterfaceThread(3【major: True】) start running sleep 2.0092681547999875 2019-05-14 14:00:05,053 - INFO: InterfaceThread(4【major: True】) start running sleep 2.266415383907653 2019-05-14 14:00:05,053 - INFO: InterfaceThread(5【major: True】) start running sleep 0.6692143957122372 2019-05-14 14:00:05,728 - INFO: InterfaceThread(5【major: True】) after sleeping 2019-05-14 14:00:06,416 - INFO: InterfaceThread(0【major: False】) after sleeping 2019-05-14 14:00:07,077 - INFO: InterfaceThread(3【major: True】) after sleeping 2019-05-14 14:00:07,329 - INFO: InterfaceThread(4【major: True】) after sleeping 2019-05-14 14:00:07,329 - INFO: InterfaceThread(4【major: True】) continue running after barrier 2019-05-14 14:00:07,329 - INFO: run by 2.284111976623535 2019-05-14 14:00:07,329 - INFO: InterfaceThread(5【major: True】) continue running after barrier 2019-05-14 14:00:07,329 - INFO: InterfaceThread(3【major: True】) continue running after barrier 2019-05-14 14:00:07,329 - WARNING: InterfaceThread(0【major: False】) watting on minorbarrier aborted 2019-05-14 14:00:08,109 - INFO: InterfaceThread(2【major: False】) after sleeping 2019-05-14 14:00:08,110 - WARNING: InterfaceThread(2【major: False】) watting on minorbarrier aborted 2019-05-14 14:00:08,613 - INFO: InterfaceThread(1【major: False】) after sleeping 2019-05-14 14:00:08,613 - WARNING: InterfaceThread(1【major: False】) watting on minorbarrier aborted
Je vois., Appelez six Threads simultanément ,Selon sleep Temps,Ça devrait être 3.5451267 Plus de deux secondes. Et en fait, Parce que les Threads importants sont à compléter , Le fil principal n'utilise que 2.284111976623535 Les secondes sont revenues . Voilà., Nous avons réalisé une amélioration significative des performances de l'interface ,Mais les fils 1、2 Parce que sleep Trop de temps, Impossible de revenir avant le retour du fil principal .