Maison  >  Article  >  développement back-end  >  Explication détaillée de l'utilisation simple du multi-threading Python, des verrous et des mécanismes d'événements

Explication détaillée de l'utilisation simple du multi-threading Python, des verrous et des mécanismes d'événements

不言
不言original
2018-04-27 11:52:121525parcourir

Cet article présente principalement l'utilisation simple du multi-threading Python, des verrous et des mécanismes d'événements. Maintenant, je le partage avec vous et lui donne une référence. Jetons un coup d'œil ensemble

Threads et processus

1. le processus a son propre espace d'adressage

2. Les threads peuvent accéder à toutes les données du processus, et les threads peuvent accéder les uns aux autres

3 Les données entre les threads sont indépendantes

4. Le processus enfant copie les données du thread

5. Le processus enfant est indépendant après son démarrage. Le processus parent ne peut que tuer le processus enfant mais ne peut pas échanger de données

. 6. Modifiez les données dans le fil. Tout affectera les autres threads et les modifications apportées au processus n'affecteront pas le processus enfant


threading.Thread

Thread est un module de threading L'une des classes les plus importantes de , vous pouvez l'utiliser pour créer des threads. Il existe deux manières de créer un thread : l'une consiste à hériter de la classe Thread et à remplacer sa méthode run ; l'autre consiste à créer un objet threading.Thread et à transmettre l'objet appelable en tant que paramètre dans sa fonction d'initialisation (__init__).

Regardons d'abord un exemple de création d'un fil de discussion en héritant de la classe threading.Thread :

import threading
import time

class MyThread(threading.Thread):
 def __init__(self, arg):
  # super(MyThread, self).__init__() # 新式类继承原有方法写法
  threading.Thread.__init__(self)
  self.arg = arg

 def run(self):
  time.sleep(2)
  print(self.arg)

for i in range(10):
 thread = MyThread(i)
 print(thread.name)
 thread.start()

Une autre façon de créer un fil de discussion :

import threading
import time

def process(arg):
 time.sleep(2)
 print(arg)

for i in range(10):
 t = threading.Thread(target=process, args=(i,))
 print(t.name)
 t.start()

La classe Thread définit également les méthodes et attributs courants suivants :

Thread.getName () Obtenez le nom du fil


Thread.setName() Définissez le nom du fil


Thread.name Nom du fil


Thread.ident Obtenez le symbole d'identité du fil. L'identifiant du thread est un entier non nul. Cet attribut n'est valide qu'après l'appel de la méthode start(). Sinon, il renvoie uniquement None

pour déterminer si le thread est actif. À partir du moment où la méthode start() est appelée pour démarrer le thread jusqu'à ce que la méthode run() soit terminée ou interrompue en raison d'une exception non gérée, le thread est actif

Thread.is_alive()

Thread. isAlive ()

Thread.join([timeout]) L'appel de Thread.join bloquera le thread appelant jusqu'à ce que le thread appelé se termine ou expire. Le paramètre timeout est de type numérique, indiquant le délai d'expiration. Si ce paramètre n'est pas fourni, le thread appelant principal se bloquera jusqu'à la fin du thread appelé


Python GIL (Global Interpreter Lock)

GIL n'est pas une fonctionnalité de Python, c'est un concept introduit lors de l'implémentation de l'analyseur Python (CPython). Tout comme le C++ est un ensemble de normes de langage (grammaire), mais il peut être compilé en code exécutable à l'aide de différents compilateurs. Compilateurs célèbres tels que GCC, INTEL C++, Visual C++, etc. Il en va de même pour Python. Le même morceau de code peut être exécuté via différents environnements d'exécution Python tels que CPython, PyPy et Psyco. Par exemple, JPython n'a pas de GIL. Cependant, CPython étant l’environnement d’exécution Python par défaut dans la plupart des environnements. Par conséquent, pour beaucoup de gens, CPython est Python, et ils tiennent pour acquis que GIL est un défaut du langage Python. Soyons donc clairs ici : GIL n’est pas une fonctionnalité de Python, et Python n’a pas du tout besoin de s’appuyer sur GIL.


Utilisation du verrouillage des threads :

# 锁:GIL 全局解释器 它是为了保证线程在运行过程中不被抢占
number = 0
lock = threading.RLock() # 创建锁


def run(num):
 lock.acquire() # 加锁
 global number
 number += 1
 print(number)
 time.sleep(2)
 lock.release() # 释放锁

for i in range(10):
 t = threading.Thread(target=run, args=(i, ))
 t.start()

Rejoindre et démon

Dans le thread principal A, le sous-thread B est créé et B.setDaemon() est appelé dans le thread principal A. Cela signifie que le thread principal A est défini comme un thread démon à ce moment-là, si le thread principal A s'exécute. Une fois terminé, que le sous-thread B soit terminé ou non, il se terminera avec le thread principal A. C'est la signification de la méthode setDaemon, qui est fondamentalement l'opposé de join. De plus, il y a une autre chose à laquelle il faut prêter une attention particulière : il doit être défini avant que la méthode start() ne soit appelée. S'il n'est pas défini comme thread démon, le programme sera suspendu indéfiniment.

class MyThread1(threading.Thread):
 def __init__(self):
  threading.Thread.__init__(self)

 def run(self):
  print("thread start")
  time.sleep(3)
  print('thread end')

print('main start')
thread1 = MyThread1()
# thread1.setDaemon(True)  # 设置子线程是否跟随主线程一起结束
thread1.start()
time.sleep(1)
print('satrt join')
# thread1.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
print('end join')

def run(n):
 print('[%s]------running----\n' % n)
 time.sleep(2)
 print('--done--')


def main():
 for i in range(5):
  t = threading.Thread(target=run, args=[i,])
  t.start()
  # t.join()
  print('starting thread', t.getName())


m = threading.Thread(target=main,args=[])
# m.setDaemon(True) # 将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务
m.start()
# m.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
print("---main thread done----")

Fil bloqué (Mutex)

Plusieurs threads peuvent être démarrés sous un seul processus. Plusieurs threads partagent l'espace mémoire du processus parent, ce qui signifie que chaque thread peut accéder aux mêmes données à ce moment-là, si deux threads le souhaitent. les mêmes données sont modifiées ?

num = 100 # 设定一个共享变量
def subNum():
 global num # 在每个线程中都获取这个全局变量
 print('--get num:', num)
 time.sleep(2)
 num -= 1 # 对此公共变量进行-1操作
thread_list = []
for i in range(100):
 t = threading.Thread(target=subNum)
 t.start()
 thread_list.append(t)
for t in thread_list: # 等待所有线程执行完毕
 t.join()
print('final num:', num)

# 加锁版本
def subNum():
 global num # 在每个线程中都获取这个全局变量
 print('--get num:', num)
 time.sleep(1)
 lock.acquire() # 修改数据前加锁
 num -= 1 # 对此公共变量进行-1操作
 lock.release() # 修改后释放

num = 100 # 设定一个共享变量
thread_list = []
lock = threading.Lock() # 生成全局锁
for i in range(100):
 t = threading.Thread(target=subNum)
 t.start()
 thread_list.append(t)
for t in thread_list: # 等待所有线程执行完毕
 t.join()
print('final num:', num)

La différence entre Rlock et Lock :

RLock permet d'être acquis plusieurs fois dans le même fil. Mais Lock ne le permet pas. Sinon, une boucle infinie se produira et le programme ne saura pas quel verrou déverrouiller. Remarque : Si vous utilisez RLock, acquire et release doivent apparaître par paires, c'est-à-dire qu'après avoir appelé acquire n fois, release doit être appelé n fois pour réellement libérer le verrou occupé

Événements

Python fournit l'objet Event pour la communication entre les threads. Il s'agit d'un indicateur de signal défini par le thread. Si l'indicateur de signal est vrai, les autres threads attendent que le signal soit contacté.

L'objet Event implémente un mécanisme de communication de thread simple. Il fournit des signaux de configuration, d'effacement, d'attente, etc. pour la communication entre les threads.

event = threading.Event() Créer un événement

1 Définir le signal

event.set()

Utilisez la méthode set() de Event pour définir le Objet événement Le drapeau de signal interne est vrai. L'objet Event fournit la méthode isSet() pour déterminer l'état de ses indicateurs de signal internes.

Lorsque la méthode set() de l'objet événement est utilisée, la méthode isSet() renvoie true

2 Signal d'effacement

event.clear()

使用Event对象的clear()方法可以清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假

3 等待
event.wait()

Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,
则wait方法一直等待到其为真时才返回。也就是说必须set新号标志位真

def do(event):
 print('start')
 event.wait()
 print('execute')

event_obj = threading.Event()
for i in range(10):
 t = threading.Thread(target=do, args=(event_obj,))
 t.start()

event_obj.clear()
inp = input('输入内容:')
if inp == 'true':
 event_obj.set()

相关推荐:

Python多线程中阻塞(join)与锁(Lock)使用误区解析

python多线程之事件Event的使用详解

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