Pythonマルチスレッド学習教材

WBOY
リリース: 2016-06-16 08:46:59
オリジナル
956 人が閲覧しました
1. Python でのスレッドの使用:
Python でスレッドを使用するには、スレッド オブジェクトをラップする関数またはクラスの 2 つの方法があります。
1. 機能的な方法: スレッド モジュールで start_new_thread() 関数を呼び出して、新しいスレッドを生成します。例:
コードをコピー コードは次のとおりです:

インポート時間
インポート スレッド
def タイマー(いいえ、間隔):
cnt = 0
while cntprint 'スレッド:(%d) 時間:%sn'%(no, time.ctime())
時間.sleep(interval)
cnt+=1
thread.exit_thread()

def test(): #thread.start_new_thread() を使用して 2 つの新しいスレッドを作成します
thread.start_new_thread(timer , (1 ,1))
thread.start_new_thread(timer, (2,2))
if __name__=='__main__':
test()

上記例では、スレッド関数タイマーを定義し、10 個の時間レコードを出力して終了します。各出力の間隔は、interval パラメーターによって決定されます。 thread.start_new_thread(function, args[, kwargs]) の最初のパラメータはスレッド関数 (この例ではタイマー メソッド) で、2 番目のパラメータはスレッド関数に渡されるパラメータであり、タプル型および kwargs である必要があります。パラメータを選択します。
スレッドは、スレッドが自然に終了するのを待つか、スレッド関数で thread.exit() メソッドまたは thread.exit_thread() メソッドを呼び出すことによって終了できます。
2. 次の例のように、threading.Thread のサブクラスを作成してスレッド オブジェクトをラップします。
コードをコピーします コードは次のとおりです。

インポート スレッド
インポート時間
class timer(threading.Thread): #タイマー クラスはクラス threading.Thread から派生します
def __init__ (self, num, interval ):
threading.Thread.__init__(self)
self.thread_num = num
self.interval = 間隔
self.thread_stop = False
def run(self ): #run() メソッドを上書きします。ここにスレッドに実行させたい内容を記述します。
while not self.thread_stop:
print 'Thread Object(%d), Time:%sn' %(self.thread_num, time .ctime())
time.sleep(self.interval)
def stop(self):
self.thread_stop = True

def test():
thread1 = タイマー(1, 1)
thread2 = timer(2, 2)
thread1.start()
thread2.start()
time.sleep(10)
thread1.stop()
thread2.stop( )
return
if __name__ == '__main__':
test()

個人的には、2 番目の方法を好みます。これは、独自のスレッド クラスを作成し、スレッド クラスのメソッドを書き換える方法です。必要に応じて、スレッドのコントロールを自分でカスタマイズできます。
threading.Thread クラスの使用法:
1. 独自のスレッド クラスの __init__ で threading.Thread.__init__(self, name = threadname) を呼び出します。
Threadname はスレッドの名前です
2 、run() は通常、必要な機能を実現するために書き換えてコードを記述する必要があります。
3, getName(), スレッドオブジェクト名を取得します
4, setName(), スレッドオブジェクト名を設定します
5, start(), スレッドを開始します
6, jion([timeout] )、待機します。他のスレッドが終了した後、再度実行します。
7、setDaemon(bool) は、サブスレッドがメインスレッドで終了するかどうかを設定し、start() の前に呼び出す必要があります。デフォルトは False です。
8、isDaemon() は、スレッドがメインスレッドで終了するかどうかを決定します。
9、isAlive()、スレッドが実行中かどうかを確認します。
さらに、スレッド モジュール自体も、スレッドの使用と管理を改善するために役立つ多くのメソッドやその他のクラスを提供します。 http://www.python.org/doc/2.5.2/lib/module-threading.html を参照してください。


2 つのスレッド オブジェクト t1 と t2 が両方とも num=0 を 1 ずつインクリメントし、t1 と t2 の両方が num を 10 回変更すると仮定します。num の最終結果は 20 になるはずです。ただし、マルチスレッドアクセスにより、num=0 の場合、t1 は num=0 を取得するという状況が発生する可能性があります。このとき、システムは t1 を「スリープ」状態にスケジュールし、t2 を「実行中」状態に変換し、t2 ページは num=0 を取得します。次に、t2 は取得した値に 1 を加えて num に代入し、num=1 とします。次に、システムは t2 を「スリープ」にスケジュールし、t1 を「実行」に切り替えます。スレッド t1 は、前に取得した 0 に 1 を加算し、それを num に代入します。このように、t1 と t2 の両方が 1 を加算する作業を 1 回完了していることは明らかですが、結果は依然として num=1 です。

上記のケースは、マルチスレッド状況で最も一般的な問題の 1 つであるデータ共有を説明しています。複数のスレッドが特定の共有データを変更する場合、データ アクセスを同期する必要があります。

1. 単純な同期

最も単純な同期メカニズムは「ロック」です。ロック オブジェクトは、threading.RLock クラスによって作成されます。スレッドはロックのacquire()メソッドを使用してロックを取得できるため、ロックは「ロック」状態になります。一度に 1 つのスレッドだけがロックを取得できます。別のスレッドがロックを取得しようとすると、ロックを所有するスレッドがロックの release() メソッドを呼び出してロックを解放するまで、そのスレッドはシステムによって「ブロック」状態に変更されます。 「ロックが解除された」状態。 「ブロック」状態のスレッドは通知を受け取り、ロックを取得する権利を持ちます。複数のスレッドが「ブロック」状態にある場合、最初にすべてのスレッドが「ブロック」状態を解放し、次にシステムが 1 つのスレッドを選択してロックを取得し、他のスレッドはサイレント (「ブロック」) 状態を続けます。
Python のスレッド モジュールとロック オブジェクトは、Python が提供する低レベルのスレッド制御ツールであり、使い方は非常に簡単です。次の例に示すように:
コードをコピー コードは次のとおりです:

インポート スレッド
インポート時間
mylock = thread .allocate_lock() #ロックを割り当てる
num=0 #共有リソース
def add_num(name):
global num
while True:
mylock.acquire() #ロックを取得します
# 共有リソースに何らかの処理を行います
print 'スレッド %s がロックされました! num=%s'%(name,str(num))
if num >= 5:
print 'スレッド % が解放されました! num=%s'%(name,str(num))
mylock.release()
thread.exit_thread()
num+=1
print 'スレッド%s が解放されました! num =%s'%(name,str(num))
mylock.release() #ロックを解放します。
def test():
thread.start_new_thread(add_num, (' A',))
thread.start_new_thread(add_num, ('B',))
if __name__== '__main__':
test()

Python も提供します高度なスレッド制御ライブラリが開発されており、これは前述のスレッド化です。 Python のスレッド モジュールは、スレッド モジュール上に構築されたモジュールであり、スレッド モジュール内の多くの属性が公開されます。スレッドモジュールでは、Python はユーザーレベルのスレッド同期ツール「Lock」オブジェクトを提供します。スレッド モジュールでは、Python は Lock オブジェクトのバリアントである RLock オブジェクトを提供します。 RLock オブジェクトは、再入可能なオブジェクトである Lock オブジェクトを内部的に維持します。 Lock オブジェクトの場合、スレッドが取得操作を 2 回続けて実行すると、最初の取得後に解放がないため、2 回目の取得でスレッドが一時停止されます。これにより、Lock オブジェクトが解放されなくなり、スレッドがデッドロックになります。 RLock オブジェクトを使用すると、スレッドが取得する回数がカウンタ変数によって内部的に維持されるため、スレッドはオブジェクトを複数回取得できます。さらに、各取得操作には、対応する解放操作が必要です。すべての解放操作が完了すると、他のスレッドが RLock オブジェクトを適用できるようになります。

スレッドの RLock オブジェクトを使用して同期を実現する方法を見てみましょう。

コードをコピー コードは次のとおりです:

インポートスレッド
mylock = threading.RLock()
num=0
class myThread(threading.Thread):
def __init__(self, name):
threading .Thread.__init__(self)
self.t_name = name
def run(self):
global num
while True:
mylock.acquire()
print 'nThread( %s) ロックされました、番号: %d'%(self.t_name, num)
if num>=4:
mylock.release()
print 'nThread(%s) がリリースされました、番号: % d'%(self.t_name, num)
break
num+=1
print 'nThread(%s) が解放されました。番号: %d'%(self.t_name, num)
mylock。 release()
def test():
thread1 = myThread('A')
thread2 = myThread('B')
thread1.start()
thread2.start()
if __name__== '__main__':
test()

共有データを変更するコードを「クリティカル セクション」と呼びます。すべての「クリティカル セクション」は、同じロック オブジェクトの取得と解放の間に囲まれている必要があります。

2. 条件付き同期

ロックは最も基本的な同期のみを提供します。特定のイベントが発生したときにのみ「クリティカル セクション」にアクセスする場合は、条件変数 Condition を使用する必要があります。
Condition オブジェクトは、Lock オブジェクトのラッパーです。Condition オブジェクトを作成するとき、そのコンストラクターはパラメーターとして Lock オブジェクトを必要とします。そのような Lock オブジェクトのパラメーターがない場合、Condition は内部で Rlock オブジェクトを作成します。もちろん、Condition オブジェクトでは、取得および解放の操作を呼び出すこともできます。これは、内部の Lock オブジェクト自体がこれらの操作をサポートしているためです。ただし、Condition の価値は、Condition が提供する待機と通知のセマンティクスにあります。
条件変数はどのように機能しますか?まず、スレッドが条件変数の取得に成功した後、この条件変数の wait() メソッドを呼び出すと、スレッドはロックを解放し、別のスレッドが同じ条件変数の notify() メソッドを呼び出すまで「ブロック」状態に入ります。 「ブロック」状態になるスレッド。この条件変数のnotifyAll()メソッドが呼び出されると、待機中のすべてのスレッドが起動されます。
プログラムまたはスレッドが常に「ブロック」状態にある場合、デッドロックが発生します。したがって、ロックや条件変数などの同期メカニズムを使用する場合は、デッドロックの発生を防ぐために慎重な検査に注意を払う必要があります。例外を生成する可能性のあるクリティカル セクションの場合は、例外処理メカニズムの Final 句を使用して、ロックが確実に解放されるようにします。条件変数を待機しているスレッドは、notify() メソッドを使用して明示的に起動する必要があります。そうしないと、永久に沈黙したままになります。すべての wait() メソッド呼び出しに、対応する Notice() 呼び出しがあることを確認してください。もちろん、念のため、notifyAll() メソッドを呼び出すこともできます。


プロデューサーとコンシューマーの問題は、典型的な同期の問題です。ここでは、2 つの異なる実装方法を簡単に紹介します。

1、条件変数
コードをコピー コードは次のとおりです:



>インポートスレッド
インポート時間
クラスプロデューサー(threading.Thread):
def __init__(self, t_name):
threading.Thread.__init__(self, name=t_name)

def run (self):
global x
con.acquire()
if x > 0:
con.wait()
else:
for i in range( 5):
x=x+1
print "producing..." + str(x)
con.notify()
print x
con.release()

class Consumer(threading.Thread):
def __init__(self, t_name):
threading.Thread.__init__(self, name=t_name)
def run(self):
global x
con.acquire()
if x == 0:
print 'consumer wait1'
con.wait()
else:
for i in range(5):
x=x-1
print "消費中..." + str(x)
con.notify()
print x
con.release()

con = threading .Condition()
x=0
print '消費者を開始'
c=Consumer('consumer')
print '生産者を開始'
p=Producer('生産者' )

p.start()
c.start()
p.join()
c.join() print x


上記の例 初期状態では、Consumer は待機状態にあり、Producer が 5 回連続して生成 (x を 1 ずつ増加) した後、待機中の Consumer に通知します。コンシューマが起動して消費を開始します (x を 1 ずつ減分します)
2、同期キュー


Python の Queue オブジェクトは、スレッド同期のサポートも提供します。 Queue オブジェクトを使用して、複数のプロデューサーと複数のコンシューマーによって形成される FIFO キューを実装できます。
プロデューサーはデータをキューに順番に格納し、コンシューマーはキューからデータを順番に取り出します。 コードをコピー コードは次のとおりです:

#プロデューサー_コンシューマー_キュー
キューインポートキューから
インポートランダム
インポートスレッド
インポート時間
#プロデューサースレッド
クラスプロデューサー(スレッド.スレッド):
def __init__(self, t_name, queue):
threading.Thread.__init__(self, name=t_name)
self.data=queue
def run(self):
for i in range( 5):
print "%s: %s はキューに %d を生成しています!n" %(time.ctime(), self.getName(), i)
self.data.put(i)
time.sleep(random.randrange(10)/5)
print "%s: %s が完了しました!" %(time.ctime(), self.getName())
#Consumer thread
class Consumer(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread .__init__(self, name=t_name)
self.data=queue
def run(self):
for i in range(5):
val = self.data.get()
print "%s: %s が消費されています。キュー内の %d が消費されています!n" %(time.ctime(), self.getName(), val)
time.sleep(random.randrange(10) ))
print "%s: %s が完了しました!" %(time.ctime(), self.getName())
#メインスレッド
def main():
queue = Queue()
プロデューサー =Producer('Pro.', queue)
consumer = Consumer('Con.', queue)
Producer.start()
consumer.start()
Producer.join()
consumer.join()
print 「すべてのスレッドが終了します!」
if __name__ == '__main__':
main()


上の例中,プロデューサー在机的時間间内生产一个“产品”,放入队列この例では、生産者が生産する速度が消費者が消費する速度よりも速いため、生産者が 1 つの「商品」を生産した後、消費者は 1 つの商品を消費します。

キュー モジュールは、マルチプロデューサとマルチコンシューマをサポートする FIFO リストを実現します。その構造関数の maxsize パラメータでその長さを設定します。 > オプションのパラメータ block が true で、timeout が None (省略値) の場合、タイムアウト時間内に使用可能なデータ要素がなくなるまで、回線はブロックされます。完全な例外です。逆に、block パラメータが false (タイムアウト パラメータ) の場合、項目はすぐに空のデータ ユニットに追加され、空のデータ ユニットがない場合は、完全な例外が発生します。 block パラメータが true で、timeout が None (省略値) の場合、timeout が 0 より大きい場合、タイムアウト時間内にデータがなくなるまで、回線はブロックされます。逆に、block パラメータが false (少しタイムアウト パラメータ) の場合は、ブロック内のデータがすぐに取り出されます。
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!