Maison >développement back-end >Tutoriel Python >Introduction détaillée au multithreading python (exemple de code)

Introduction détaillée au multithreading python (exemple de code)

不言
不言avant
2019-01-10 11:47:222218parcourir

Le contenu de cet article est une introduction détaillée (exemple de code) sur le multi-threading Python. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

verrouillage global de l'interpréteur (cpython)

Un seul thread est en cours d'exécution sur un processeur exécutant le bytecode en même temps (plusieurs threads ne peuvent pas être mappés sur plusieurs processeurs)

import dis

def add(a):
    a = a + 1
    return a
print(dis.dis(add))

GIL sera publié dans certains cas

Les résultats sont différents à chaque fois. Problèmes de sécurité entre les threads

GIL sera publié en fonction du nombre de lignes directes de code exécutées ou du temps. slice release GIL

Libération active lors de la rencontre d'opérations d'E/S

total = 0

def add():
    #1. dosomething1
    #2. io操作
    # 1. dosomething3
    global total
    for i in range(1000000):
        total += 1
def desc():
    global total
    for i in range(1000000):
        total -= 1

import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

Programmation multi-thread

La plus petite unité que le système d'exploitation peut planifier est le processus, car le processus est très important pour le système. La consommation de ressources est très importante, elle a donc évolué plus tard vers les threads. Les threads dépendent en fait de nos processus (ce que nous pouvons réellement voir dans le gestionnaire de tâches, ce sont en fait les processus. La plus petite unité que le système d'exploitation peut planifier). est un fil.

Pour la programmation axée sur les opérations d'E/S, la différence de performances entre la sortie multi-processus et multi-first n'est pas grande. Les performances du multi-threading sont encore supérieures à celles du multi-processus, car multi-thread. la programmation est plus légère.

Thread simple

import time
from threading import Thread


def get_detail_html(url):
    print("get detail html started")
    time.sleep(2)
    print("get detail html end")


def get_detail_url(url):
    print("get detail url started")
    time.sleep(4)
    print("get detail url end")


if __name__ == '__main__':
    thread1 = Thread(target=get_detail_html, args=("",))
    thread2 = Thread(target=get_detail_url, args=("",))
    # 设置为守护线程 当主线程运行完时 子线程被kill掉
    thread1.setDaemon(True)
    thread2.setDaemon(True)
    start_time = time.time()
    thread1.start()
    thread2.start()

    # 设置为阻塞 等待线程运行完再关闭主线程
    thread1.join()
    thread2.join()
    # 默认情况下 主线程退出与时 子线程不会被kill掉
    print("last time: {}".format(time.time() - start_time))

Thread surchargé pour implémenter le multi-threading

import time
import threading

def get_detail_html(url):
    print("get detail html started")
    time.sleep(2)
    print("get detail html end")


def get_detail_url(url):
    print("get detail url started")
    time.sleep(4)
    print("get detail url end")


#2. 通过集成Thread来实现多线程
class GetDetailHtml(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("get detail html started")
        time.sleep(2)
        print("get detail html end")


class GetDetailUrl(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("get detail url started")
        time.sleep(4)
        print("get detail url end")

if  __name__ == "__main__":
    thread1 = GetDetailHtml("get_detail_html")
    thread2 = GetDetailUrl("get_detail_url")
    start_time = time.time()
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

    #当主线程退出的时候, 子线程kill掉
    print ("last time: {}".format(time.time()-start_time))

Communication entre multi-threads

Utiliser la file d'attente

# filename: thread_queue_test.py
# 通过queue的方式进行线程间同步
from queue import Queue
import time
import threading


def get_detail_html(queue):
    # 死循环 爬取文章详情页
    while True:
        url = queue.get()
        # for url in detail_url_list:
        print("get detail html started")
        time.sleep(2)
        print("get detail html end")


def get_detail_url(queue):
    # 死循环 爬取文章列表页
    while True:
        print("get detail url started")
        time.sleep(4)
        for i in range(20):
            # put 等到有空闲位置 再放入
            # put_nowait 非阻塞方式
            queue.put("http://projectsedu.com/{id}".format(id=i))
        print("get detail url end")


# 1. 线程通信方式- 共享变量
if __name__ == "__main__":
    detail_url_queue = Queue(maxsize=1000)

    thread_detail_url = threading.Thread(target=get_detail_url, args=(detail_url_queue,))
    for i in range(10):
        html_thread = threading.Thread(target=get_detail_html, args=(detail_url_queue,))
        html_thread.start()

    start_time = time.time()
    # 调用task_down从主线程退出
    detail_url_queue.task_done()
    # 从queue的角度阻塞
    detail_url_queue.join()

    print("last time: {}".format(time.time() - start_time))

Problèmes de synchronisation des threads

Problèmes auxquels il faut faire face dans la programmation multi-thread

La raison pour laquelle aucun verrou n'est dangereux

# 没有锁
def add1(a):
    a += 1

def desc1(a):
    a -= 1

"""add
1. load a  a = 0
2. load 1  1
3. +    1
4. 赋值给a a=1
"""

"""add
1. load a  a = 0
2. load 1  1
3. -    1
4. 赋值给a a=-1
"""
import dis
print(dis.dis(add1))
print(dis.dis(desc1))

Verrouillage ordinaire (verrouillage )

L'utilisation de verrous affectera les performances et les verrous provoqueront un blocage (acquérir le verrou deux fois, ne pas le libérer après avoir acquis le verrou, s'attendre l'un l'autre (a a besoin des ressources de b et b a besoin des ressources de a))

import threading
from threading import Lock

total = 0
# 定义一把锁
lock = Lock()
def add():
    global total
    global lock
    for i in range(1000000):
        # 获取锁
        lock.acquire()
        total += 1
        # 释放锁
        lock.release()

def desc():
    global total
    for i in range(1000000):
        lock.acquire()
        total -= 1
        lock.release()

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

S'attendre (concours de ressources)

"""
A(a、b)
acquire (a)
acquire (b)

B(a、b)
acquire (b)
acquire (a)

# 解决办法
B(a、b)
acquire (a)
acquire (b)
"""

Verrouillage réentrant (Rlock)

import threading
from threading import RLock

total = 0
# 可重入锁 可以在同一个线程中可载入多次
lock = RLock()
def add(lock):
    global total
    for i in range(1000000):
        # 获取锁
        lock.acquire()
        lock.acquire()
        total += 1
        do_something(lock)
        # 释放锁
        lock.release()
        lock.release()

def desc():
    global total
    for i in range(1000000):
        lock.acquire()
        total -= 1
        lock.release()

def do_something(lock):
    lock.acquire()
    # do something
    lock.release()

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

Condition de verrouillage variable (condition)

Utiliser la synchronisation entre des threads complexes

# 没有条件锁 不能实现对话
import threading


class XiaoAi(threading.Thread):
   def __init__(self, lock):
       super().__init__(name="小爱")
       self.lock = lock

   def run(self):
       self.lock.acquire()
       print("{} : 在 ".format(self.name))
       self.lock.release()

       self.lock.acquire()
       print("{} : 好啊 ".format(self.name))
       self.lock.release()


class TianMao(threading.Thread):
   def __init__(self, lock):
       super().__init__(name="天猫精灵")
       self.lock = lock

   def run(self):
       self.lock.acquire()
       print("{} : 小爱同学 ".format(self.name))
       self.lock.release()

       self.lock.acquire()
       print("{} : 我们来对古诗吧 ".format(self.name))
       self.lock.release()


if __name__ == "__main__":
   cond = threading.Condition()
   xiaoai = XiaoAi(cond)
   tianmao = TianMao(cond)

   xiaoai.start()
   tianmao.start()
# 条件锁
import threading


class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="小爱")
        self.cond = cond

    def run(self):
        with self.cond:
            self.cond.wait()
            print("{} : 在 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 好啊 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 君住长江尾 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 共饮长江水 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 此恨何时已 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 定不负相思意 ".format(self.name))
            self.cond.notify()

class TianMao(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="天猫精灵")
        self.cond = cond

    def run(self):
        with self.cond:
            print("{} : 小爱同学 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 我们来对古诗吧 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 我住长江头 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 日日思君不见君 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 此水几时休 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 只愿君心似我心 ".format(self.name))
            self.cond.notify()
            self.cond.wait()



if __name__ == "__main__":
    from concurrent import futures
    cond = threading.Condition()
    xiaoai = XiaoAi(cond)
    tianmao = TianMao(cond)

    # 启动顺序很重要
    # 在调用with cond之后才能调用wait或者notify方法
    # condition有两层锁, 一把底层锁会在线程调用了wait方法的时候释放,
    # 上面的锁会在每次调用wait的时候分配一把并放入到cond的等待队列中,
    # 等到notify方法的唤醒
    xiaoai.start()
    tianmao.start()

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