ホームページ  >  記事  >  バックエンド開発  >  Pythonのマルチスレッド、ロック、イベントの仕組みの簡単な使い方を詳しく解説

Pythonのマルチスレッド、ロック、イベントの仕組みの簡単な使い方を詳しく解説

不言
不言オリジナル
2018-04-27 11:52:121526ブラウズ

この記事では主に Python のマルチスレッド、ロック、イベントの仕組みについて簡単に紹介しますので、参考にしてください。一緒に見てみましょう

スレッドとプロセス

1. スレッドは、それを作成したプロセスのアドレス空間を共有し、プロセスは独自のアドレス空間を持ちます


2.プロセスのデータとスレッドは相互にアクセスできます

3. スレッド間のデータは独立しています

4. 子プロセスは、開始後にスレッドのデータをコピーします

5.プロセスは子プロセスを強制終了することしかできませんが、データを交換することはできません。

6. スレッド内のデータを変更すると他のスレッドに影響しますが、プロセスへの変更は子プロセスには影響しません


threading.Thread

Threadスレッド モジュール 1 の中で最も重要なクラスです。これを使用してスレッドを作成できます。スレッドを作成するには 2 つの方法があります。1 つは Thread クラスを継承し、その run メソッドをオーバーライドする方法です。もう 1 つは、threading.Thread オブジェクトを作成し、呼び出し可能なオブジェクトをその初期化関数 (__init__) のパラメーターとして渡す方法です。

まず、スレッドを継承してスレッドを作成する例を見てみましょう。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()

スレッドを作成する別の方法:

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()

Thread クラスは、次の一般的なメソッドも定義します。および属性:

Thread.getName() スレッド名を取得します


Thread.setName() スレッド名を設定します


Thread.name スレッド名


Thread.ident スレッドの識別子を取得します。スレッド識別子はゼロ以外の整数です。それ以外の場合は、スレッドがアクティブかどうかを判断するために None

を返すだけです。スレッドを開始するために start() メソッドが呼び出されてから、run() メソッドが完了するか、未処理の例外によって中断されるまで、スレッドはアクティブ化されます

Thread.is_alive()

Thread.isAlive()

Thread . join([timeout]) Thread.join を呼び出すと、呼び出されたスレッドが実行を終了するかタイムアウトになるまで、呼び出し元のスレッドがブロックされます。パラメータ timeout はタイムアウト時間を示す数値型です。このパラメータが指定されていない場合、呼び出されたスレッドは、呼び出されたスレッドが終了するまでブロックされます


Python GIL (Global Interpreter Lock)

GIL は、 Python の機能。これは、Python パーサー (CPython) を実装するときに導入された概念です。 C++ が一連の言語 (文法) 標準であるのと同様に、さまざまなコンパイラを使用して実行可能コードにコンパイルできます。 GCC、INTEL C++、Visual C++などの有名なコンパイラ。 Python にも同じことが当てはまります。CPython、PyPy、Psyco などの異なる Python 実行環境を通じて、同じコードを実行できます。たとえば、JPython には GIL がありません。ただし、ほとんどの環境では CPython がデフォルトの Python 実行環境です。したがって、多くの人の概念では、CPython は Python であり、GIL は Python 言語の欠陥であると当然のことと考えています。ここで明確にしておきますが、GIL は Python の機能ではなく、Python は GIL に依存する必要はまったくありません。


スレッドロックの使用法:

# 锁: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()

Join & Daemon

メインスレッドAでサブスレッドBが作成され、メインスレッドAでB.setDaemon()が呼び出されます。は、メインスレッド A がデーモンスレッドとして設定されていることを意味します。このとき、メインスレッド A の実行が終了すると、サブスレッド B が完了するかどうかに関係なく、メインスレッド A とともに終了します。 setDaemon メソッドの基本的には join と同じですが、その逆です。さらに、特に注意すべき点がもう 1 つあります。start() メソッドを呼び出す前に設定する必要があります。デーモン スレッドとして設定されていない場合、プログラムは無期限に停止されます。

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----")

スレッドロック(ミューテックス)

1つのプロセスの下で複数のスレッドを開始でき、複数のスレッドが親プロセスのメモリ空間を共有します。つまり、各スレッドはアクセスできます。このとき、2 つのスレッドが同じデータを同時に変更したい場合はどうなりますか?

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)

Rlock と Lock の違い:

RLock は同じスレッド内で複数回取得できます。しかし、ロックはそれを許しません。そうしないと、無限ループが発生し、プログラムはどのロックを解除すればよいのかわかりません。注: RLock を使用する場合、acquire と release はペアで指定する必要があります。つまり、acquire が n 回呼び出された場合、占有されているロックを実際に解放するには release を n 回呼び出す必要があります

Events

Python は Event オブジェクトを提供しますスレッド間通信の場合、これはスレッドによって設定されたシグナル フラグです。シグナル フラグが true の場合、他のスレッドはシグナルに接続されるまで待機します。

イベントオブジェクトは、スレッド間の通信を実現するためのシグナルの設定、シグナルのクリア、待機などを提供する単純なスレッド通信メカニズムを実装します。

event = threading.Event() イベントを作成します

1 シグナルを設定します

event.set()

Eventのset()メソッドを使用して、Eventオブジェクト内のシグナルフラグをtrueに設定します。 Event オブジェクトは、内部信号フラグのステータスを決定するための isSet() メソッドを提供します。

イベントオブジェクトのset()メソッドを使用した場合、isSet()メソッドはtrueを返す

2 シグナルをクリア

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的使用详解

以上がPythonのマルチスレッド、ロック、イベントの仕組みの簡単な使い方を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。