ホームページ  >  記事  >  バックエンド開発  >  Python でのマルチスレッドのサンプル チュートリアル

Python でのマルチスレッドのサンプル チュートリアル

WBOY
WBOYオリジナル
2016-06-16 08:42:291060ブラウズ

この記事では、Python でのマルチスレッドの使用法を例を挙げて詳しく説明し、Python プログラミングの幅広い用途について説明します。皆さんの参考に共有してください。具体的な分析は次のとおりです。

Python のマルチスレッド操作は、thread モジュールとスレッド モジュールを使用して実装できます。Py3 では thread モジュールの名前が _thread に変更され、推奨されなくなりました。スレッディングモジュールはスレッドの上にカプセル化されており、推奨されるマルチスレッドモジュールでもあります。この記事では主にスレッディングモジュールをベースに紹介します。バージョンによっては、thread モジュールが存在しない場合があります。threading モジュールの代わりに dump_threading を使用します。

1. スレッドの作成

スレッド モジュールの各スレッドは Thread オブジェクトです。スレッドを作成するには、関数を Thread オブジェクトに渡して実行する方法と、Thread から継承して run メソッドをオーバーライドする方法があります。 (はい、Java とはあまり似ていません)。

以下の 2 つのメソッドを使用してスレッドを作成し、同時に実行します

import random, threading
def threadFunction():
  for i in range(10):
    print 'ThreadFuction - %d'%i
    time.sleep(random.randrange(0,2))


class ThreadClass(threading.Thread):
  def __init__(self):
    threading.Thread.__init__(self);
    
  def run(self):
    for i in range(10):
      print 'ThreadClass - %d'%i
      time.sleep(random.randrange(0,2))

if __name__ == '__main__':
  tFunc = threading.Thread(target = threadFunction);
  tCls = ThreadClass()
  tFunc.start()
  tCls.start()

実行結果は以下の通りで、2つのスレッドが交互に印刷していることがわかります。空白行や 1 行に複数の出力がある場合は、Py の print がスレッドセーフではないため、現在のスレッドの print が内容の一部を出力した後、別のスレッドの print によってプリエンプトされ、改行の前に出力されます。コンテンツ。

ThreadFuction - 0
ThreadFuction - 1
ThreadFuction - 2
ThreadClass - 0
ThreadFuction - 3
ThreadClass - 1
ThreadFuction - 4
ThreadClass - 2
ThreadClass - 3
ThreadClass - 4ThreadFuction - 5

ThreadClass - 5
ThreadClass - 6
ThreadClass - 7
ThreadClass - 8
ThreadFuction - 6ThreadClass - 9

ThreadFuction - 7
ThreadFuction - 8
ThreadFuction - 9

Thread クラスのコンストラクターは次のように定義されています

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})

グループ: ThreadGroup 拡張機能の使用のために予約されており、通常は役に立ちません
target: 新しいスレッドのタスク関数名
name: スレッド名、通常は役に立ちません
args: タプルパラメータ
kwargs: 辞書パラメータ

Threadクラスのメンバー変数と関数は以下のとおりです

start() スレッドを開始します
run() スレッドの実行本体も一般に書き換えが必要なものです
join([timeout]) はスレッドが終了するまで待機します
name スレッド名
ident スレッド ID
デーモン スレッドを保護するかどうか
isAlive()、is_alive() スレッドが生きているかどうか
getName()、setName() 名前のget&setメソッド
isDaemon()、setDaemon() デーモンの get&set メソッド

ここでのデーモン スレッドは、Linux のデーモン プロセスと同じ概念ではありません。これは、すべてのデーモン スレッドが終了した後でのみメイン プログラムが終了することを意味します。それ以外の場合、スレッド タスクが終了していなくても、デーモン スレッドでない限り、メイン プログラムとともに終了します。 Linux のデーモン プロセスの定義はその逆で、デーモン プロセスは親プロセスから分離されており、親プロセスが終了しても終了しません。

2. スレッドの同期

スレッドの同期は、マルチスレッドの中核的な問題です。スレッド モジュールは、スレッド固有のデータ、セマフォ、ミューテックス ロック、条件変数などを含むスレッドの同期を適切にサポートしています。

1. スレッド固有のデータ

つまり、スレッド固有のデータはスレッドのみが保持するグローバル変数であり、相互に変更しても相互に影響を与えることはありません。

local() メソッドは、スレッド独立オブジェクトを生成するためにスレッド モジュールで使用されます。たとえば、sleep(1) は、子スレッドが最初に実行を終了してから次のステートメントを実行することを保証します。

data = threading.local()
def threadFunction():
  global data
  data.x = 3
  print threading.currentThread(), data.x
  
if __name__ == '__main__':
  data.x = 1
  tFunc = threading.Thread(target = threadFunction).start();
  time.sleep(1)
  print threading.current_thread(), data.x

<Thread(Thread-1, started 36208)> 3
<_MainThread(MainThread, started 35888)> 1

出力は上記のとおりです。Thread-1 の data.x の変更は、メインスレッドの data.x の値に影響を与えないことがわかります。

2. ミューテックスロック

スレッドでは、threading.Lock と threading.RLock という 2 つのロックが定義されています。 2 つの違いは、後者がリエントラント ロックであることです。つまり、スレッド内で同じロックを繰り返しロックしてもデッドロックは発生しません。これは、再帰的ロックである POSIX の PTHREAD_MUTEX_RECURSIVE と同じ概念です。

ミューテックス ロックの API は非常にシンプルで、ロックの割り当て、ロック、ロック解除の 3 つの関数のみを備えています。

threading.Lock() ミューテックスを割り当てます
acquire([blocking=1]) Lock (ブロッキングまたは非ブロッキング。非ブロッキングの場合は try_lock と同等。False が返された場合は、他のスレッドによってロックされていることを示します。)
release() ロック解除
以下では、例を使用してミューテックス ロックの使用法を説明します。前の例では、マルチスレッド印刷により出力が混乱します。ここでは、1 行に 1 つの出力のみが存在するようにするために、ミューテックス ロックが使用されています。

def threadFunction(arg):
  while True:
    lock.acquire()
    print 'ThreadFuction - %d'%arg
    lock.release()

if __name__ == '__main__':
  lock = threading.Lock()
  threading.Thread(target = threadFunction, args=(1,)).start();
  threading.Thread(target = threadFunction, args=(2,)).start();

3. 条件変数

条件変数は常にミューテックス ロックと一緒に使用されます。スレッド化の条件変数は、デフォルトで RLock にバインドされます。条件変数を初期化するときに、独自に定義したロックを渡すこともできます。

利用できる機能は以下の通りです

threading.Condition([lock]) 分配一个条件变量
acquire(*args)        条件变量上锁
release()          条件变量解锁
wait([timeout])       等待唤醒,timeout表示超时
notify(n=1)         唤醒最大n个等待的线程
notifyAll()、notify_all()  唤醒所有等待的线程
下面这个例子使用条件变量来控制两个线程交替运行

num = 0
def threadFunction(arg):
  global num
  while num < 10:
    cond.acquire()
    while num % 2 != arg:
      cond.wait()
    print 'Thread %d - %d' %(arg, num)
    num += 1
    cond.notify()
    cond.release()

if __name__ == '__main__':
  cond = threading.Condition()
  threading.Thread(target = threadFunction, args=(0,)).start();
  threading.Thread(target = threadFunction, args=(1,)).start();

出力は次のとおりです

Thread 0 - 0
Thread 1 - 1
Thread 0 - 2
Thread 1 - 3
Thread 0 - 4
Thread 1 - 5
Thread 0 - 6
Thread 1 - 7
Thread 0 - 8
Thread 1 - 9
Thread 0 - 10

実は上記のプログラムには問題があり、出力したいのは0~9なのですが、実際には10も出力されてしまうのです。理由は非常に単純で、2つのスレッドが交互に出力するため、numが追加される可能性があります。 1 つのスレッドでは 2 であり、結果として 10 が印刷されるため、印刷する前に再度確認する必要があります。

この記事で説明されている内容は、皆さんの Python プログラミング設計にとって一定の参考になると思います。

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