Python マルチスレッドの詳細な紹介 (コード例)

不言
リリース: 2019-01-10 11:47:22
転載
2208 人が閲覧しました

この記事では Python のマルチスレッドについて詳しく紹介 (コード例) しています。一定の参考価値があります。困っている友人は参考にしてください。お役に立てれば幸いです。

グローバル インタプリタ ロック (cpython)

バイトコードの実行と同時に 1 つの CPU 上で実行されるスレッドは 1 つだけです (複数のスレッドを複数の CPU にマッピングすることはできません)

import dis

def add(a):
    a = a + 1
    return a
print(dis.dis(add))
ログイン後にコピー

GIL は場合によってはリリースされます

結果は毎回異なります。スレッド間のセキュリティの問題

##GIL は、直接コード行数または実行されたタイム スライスに基づいてリリースされます。リリースGIL

IO 操作が発生したときに積極的に解放する

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)
ログイン後にコピー
マルチスレッド プログラミング

プロセスは大量のデータを消費するため、オペレーティング システムがスケジュールできる最小単位はプロセスです。システム リソース。大きいため、後にスレッドに進化しました。スレッドは実際にはプロセスに依存します (タスク マネージャーで実際に確認できるのは、実際にはプロセスです)。オペレーティング システムがスケジュールできる最小単位はスレッドです。

IO 操作に重点を置いたプログラミングの場合、マルチプロセスとマルチファースト出力のパフォーマンスの差は大きくありません。マルチスレッドのパフォーマンスはマルチプロセスよりもさらに高くなります。プログラミングはより軽量です。

単純なスレッド

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))
ログイン後にコピー

マルチスレッドを実装するためのオーバーロードされたスレッド

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))
ログイン後にコピー

複数のスレッド間の通信

キューの使用

# 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))
ログイン後にコピー
スレッドの同期問題

マルチスレッド プログラミングで直面する必要がある問題

ロックが安全でない理由

# 没有锁
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))
ログイン後にコピー

通常のロック (Lock)

ロックの使用パフォーマンスに影響し、ロックはデッドロックを引き起こします (ロックを 2 回取得する、ロックを取得した後に解放しない、お互いを待機する (a には b のリソースが必要、b には 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)
ログイン後にコピー
お互いを待機 (リソース 競合)

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

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

# 解决办法
B(a、b)
acquire (a)
acquire (b)
"""
ログイン後にコピー

リエントラント ロック (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)
ログイン後にコピー

条件変数ロック (条件)

複雑なスレッド間同期に使用されます

# 没有条件锁 不能实现对话
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()
ログイン後にコピー

以上がPython マルチスレッドの詳細な紹介 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート