Python マルチスレッド


マルチスレッドは、複数の異なるプログラムを同時に実行することに似ています。 マルチスレッドには次の利点があります:

  • スレッドを使用すると、長期プログラムのタスクをバックグラウンドで処理できます。

  • ユーザー インターフェイスをより魅力的にすることができ、ユーザーがボタンをクリックして特定のイベントの処理をトリガーすると、プログレス バーがポップアップして処理の進行状況を表示できます

  • プログラムが高速化される可能性があります

  • スレッドは、ユーザー入力、ファイルの読み取りと書き込み、ネットワークでのデータの送受信などの待機中のタスクの実装でより便利です。この場合、メモリ使用量などの貴重なリソースを解放できます。

スレッドは、実行中のプロセスとは依然として異なります。それぞれの独立したスレッドには、プログラム実行のエントリ ポイント、順次実行シーケンス、およびプログラムの終了ポイントがあります。ただし、スレッドは独立して実行できず、アプリケーション プログラム内に存在する必要があり、アプリケーション プログラムは複数のスレッドの実行制御を提供します。

各スレッドには、スレッドのコンテキストと呼ばれる独自の CPU レジスタのセットがあり、スレッドが最後に実行されたときの CPU レジスタの状態を反映します。

命令ポインタとスタック ポインタ レジスタは、スレッド コンテキストで最も重要な 2 つのレジスタであり、スレッドは常にプロセス コンテキストで実行されます。これらのアドレスは、スレッドを所有するプロセスのアドレス空間内のメモリをマークするために使用されます。

  • スレッドはプリエンプト (中断) できます。

  • 他のスレッドの実行中に、スレッドを一時的に保留 (スリープとも呼ばれる) することができます。これをスレッド バックオフといいます。


Python スレッドの学習を始めましょう

Python でスレッドを使用するには、スレッド オブジェクトをラップする関数またはクラスの 2 つの方法があります。

機能: スレッドモジュールの start_new_thread() 関数を呼び出して、新しいスレッドを生成します。構文は次のとおりです:

thread.start_new_thread ( function, args[, kwargs] )

パラメータの説明:

  • function - スレッド関数。

  • args - スレッド関数に渡されるパラメータ。タプル型である必要があります。

  • kwargs - オプションのパラメータ。

例:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import thread
import time

# 为线程定义一个函数
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print "%s: %s" % ( threadName, time.ctime(time.time()) )

# 创建两个线程
try:
   thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print "Error: unable to start thread"

while 1:
   pass

上記のプログラムを実行した出力結果は次のとおりです:

Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009

スレッドの終了は、通常、スレッド関数の自然な終了に依存します。また、スレッド関数で thread.exit() を呼び出すこともできます。スレッド関数。スレッドを終了する目的を達成するために SystemExit 例外をスローします。


スレッド モジュール

Python は、スレッドとスレッドという 2 つの標準ライブラリを通じてスレッドのサポートを提供します。 thread は、低レベルのプリミティブなスレッドと単純なロックを提供します。

スレッド モジュールによって提供されるその他のメソッド:

  • threading.currentThread(): 現在のスレッド変数を返します。

  • threading.enumerate(): 実行中のスレッドを含むリストを返します。実行中とは、スレッドの開始後から終了までを指します。ただし、起動前と終了後のスレッドは除きます。

  • threading.activeCount(): 実行中のスレッドの数を返します。これは、len(threading.enumerate()) と同じ結果になります。

使用方法に加えて、スレッド モジュールはスレッドを処理するための Thread クラスも提供します。 Thread クラスは次のメソッドを提供します:

  • run(): スレッドのアクティビティを表すために使用されるメソッド。

  • start():スレッドアクティビティを開始します。


  • join([time]): スレッドが終了するまで待ちます。これにより、スレッドの join() メソッドが異常終了 (通常終了するか、未処理の例外をスロー) が呼び出されるか、またはオプションのタイムアウトが発生するまで、呼び出しスレッドがブロックされます。

  • isAlive(): スレッドがアクティブかどうかを返します。

  • getName(): スレッド名を返します。

  • setName(): スレッド名を設定します。


Threadingモジュールを使用してスレッドを作成します

Threadingモジュールを使用してスレッドを作成し、threading.Threadから直接継承し、__init__メソッドとrunメソッドを書き換えます:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading
import time

exitFlag = 0

class myThread (threading.Thread):   #继承父类threading.Thread
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):                   #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数 
        print "Starting " + self.name
        print_time(self.name, self.counter, 5)
        print "Exiting " + self.name

def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            thread.exit()
        time.sleep(delay)
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启线程
thread1.start()
thread2.start()

print "Exiting Main Thread"

上記の実行結果プログラムは次のとおりです。

Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2


スレッドの同期

複数のスレッドが共同して特定のデータを変更すると、予測できない結果が発生する可能性があります。データの正確性を確保するには、複数のスレッドを同期する必要があります。

単純なスレッド同期は、Thread オブジェクトの Lock と Rlock を使用することで実現できます。両方のオブジェクトには、一度に 1 つのスレッドのみで操作する必要があるデータの場合、操作を Acquire メソッドと Release メソッドに配置できます。メソッド間のリリース。以下の通り:

マルチスレッドの利点は、複数のタスクを同時に実行できることです (少なくともそう感じられます)。ただし、スレッドがデータを共有する必要がある場合、データの非同期の問題が発生する可能性があります。

次の状況を考えてみましょう。リスト内のすべての要素が 0 で、スレッド「set」は後ろから前にすべての要素を 1 に変更し、スレッド「print」はリストを前から後ろに読み取って印刷する責任があります。

その後、おそらくスレッド「set」が変更を開始すると、スレッド「print」がリストを出力し、出力は半分が 0 で半分が 1 になります。これがデータの非同期です。この状況を回避するために、ロックの概念が導入されました。

ロックには、ロックとロック解除の 2 つの状態があります。 「set」などのスレッドが共有データにアクセスしたい場合は、まずロックを取得する必要があります。「print」などの別のスレッドがすでにロックを取得している場合は、スレッド「set」を一時停止させます。これは同期ブロックです。スレッド「Print」まで アクセスが完了してロックが解除された後、スレッド「set」を続行させます。

この処理により、リストを印刷する際に全て0か全て1が出力されるようになり、半分0と半分1という恥ずかしい場面はなくなりました。

例:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading
import time

class myThread (threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print "Starting " + self.name
       # 获得锁,成功获得锁定后返回True
       # 可选的timeout参数不填时将一直阻塞直到获得锁定
       # 否则超时后将返回False
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # 释放锁
        threadLock.release()

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1

threadLock = threading.Lock()
threads = []

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()

# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有线程完成
for t in threads:
    t.join()
print "Exiting Main Thread"


スレッド優先度キュー (キュー)

Python の Queue モジュールは、FIFO (先入れ先出し) キュー Queue、LIFO (後入れ先出し) キュー LifoQueue などの同期のスレッドセーフ キュー クラスを提供します。優先キューPriorityQueue。これらのキューはロック プリミティブを実装しており、マルチスレッドで直接使用できます。キューを使用して、スレッド間の同期を実現できます。

Queue モジュールでよく使用されるメソッド:


  • Queue.qsize() キューのサイズを返します

  • Queue.empty() キューが空の場合は True を返し、それ以外の場合は False を返します

  • Queue.full() キューがいっぱいの場合は戻りますTrue、そうでない場合は False

  • Queue.full は maxsize size に対応します

  • Queue.get([block[, timeout]]) でキューを取得し、タイムアウト待ち時間

  • Queue.get_nowait() は同等ですQueue.get(False)へ

  • Queue.put(item)はキューに書き込み、タイムアウト待ち時間

  • Queue.put_nowait(item)はQueue.put(item, False)と同等です

  • Queue.task_done() ジョブの完了後、Queue.task_done() 関数はタスクが完了したキューにシグナルを送信します

  • Queue.join() は実際には、他の操作を実行する前にキューが空になるまで待機することを意味します

例:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import Queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print "Starting " + self.name
        process_data(self.name, self.q)
        print "Exiting " + self.name

def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print "%s processing %s" % (threadName, data)
        else:
            queueLock.release()
        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1

# 创建新线程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 填充队列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待队列清空
while not workQueue.empty():
    pass

# 通知线程是时候退出
exitFlag = 1

# 等待所有线程完成
for t in threads:
    t.join()
print "Exiting Main Thread"

上記プログラム 実行結果:

Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread