Pythonスレッド同期の実現コード


本論文はPythonにおけるスレッド同期オブジェクトを紹介し、主にthreadとthreadingモジュールに関するものである。
threadingモジュールが提供するスレッド同期の原語は、ロック、RLock、Condation、Event、Semaphoreなどのオブジェクトを含む。
スレッド実行
joinとset Daemen
サブスレッドは、メインスレッドの運転が終了したら、継続して実行されます。サブスレッドに守護スレッド(setDaemen=True)を設定すると、メインスレッドの運転が終了します。
join()スレッドの場合、メインラインは、サブスレッドの実行が完了するまで待機します。

import threading
import time


def get_thread_a():
 print("get thread A started")
 time.sleep(3)
 print("get thread A end")


def get_thread_b():
 print("get thread B started")
 time.sleep(5)
 print("get thread B end")


if __name__ == "__main__":
 thread_a = threading.Thread(target=get_thread_a)
 thread_b = threading.Thread(target=get_thread_b)
 start_time = time.time()
 thread_b.setDaemon(True)
 thread_a.start()
 thread_b.start()
 thread_a.join()
 
 end_time = time.time()
 print("execution time: {}".format(end_time - start_time))
スリーロード.aはjoinで、まず子スレッドthread_a執行、thread_bは守護スレッドであり、メインスレッドが実行された後、thread_b実行しない結果は以下の通りである。
get thread A started
get thread B started
get thread A end
execution time:3.03199 9815721
スレッド同期
スレッド間でグローバル変数を共有する場合、複数のスレッドがこの変数に対して異なる動作を行う場合、この変数の最終的な結果は不確定である可能性があります(スレッド実行後の結果によっては異なります)。
python対スレッドロックは主にロックとRlockモジュールがあります。
ロック: 

from threading import Lock
lock = Lock()
lock.acquire()
lock.release() 
ロックにはacquire()とrelease()の方法があります。この二つの方法はペアで現れなければなりません。acquire()の後はrelease()の後でないとacquire()ができません。
Rlock:
ロックがデッドロックの原因となる可能性がある場合には、RLockはロックを改善し、RLockは同じスレッド内で複数回acquireを呼び出すことができますが、同じ回数のrelease()を再実行しなければなりません。

from threading import RLock
lock = RLock()
lock.acquire()
lock.acquire()
lock.release()
lock.release() 
condition(条件変数)は、スレッドが実行されている時に、特定の条件を満たすと、関連データにアクセスできます。

import threading

def get_thread_a(condition):
 with condition:
  condition.wait()
  print("A : Hello B,that's ok")
  condition.notify()
  condition.wait()
  print("A : I'm fine,and you?")
  condition.notify()
  condition.wait()
  print("A : Nice to meet you")
  condition.notify()
  condition.wait()
  print("A : That's all for today")
  condition.notify()

def get_thread_b(condition):
 with condition:
  print("B : Hi A, Let's start the conversation")
  condition.notify()
  condition.wait()
  print("B : How are you")
  condition.notify()
  condition.wait()
  print("B : I'm fine too")
  condition.notify()
  condition.wait()
  print("B : Nice to meet you,too")
  condition.notify()
  condition.wait()
  print("B : Oh,goodbye")

if __name__ == "__main__":
 condition = threading.Condition()
 thread_a = threading.Thread(target=get_thread_a, args=(condition,))
 thread_b = threading.Thread(target=get_thread_b, args=(condition,))
 thread_a.start()
 thread_b.start() 
Conditionの内部には一つのロックがあります。標準はRLockです。wait()とnotify()を呼び出す前に、まずacquire()を呼び出してこの錠を取ってから実行できます。wait()とnotify()が実行されたら、release()を呼び出してこのロックを解除し、with conditionを実行する時、先にacquire()、withを実行します。だからconditionには二重ロックがあります。一番下のロックはwait()を呼び出した時にリリースされます。同時に待ち行列にロックをかけて、notify()を起動して解除錠をかけます。
wait():条件変数の通知を待つことができます。notify()は起動できます。
notify():待ち行列waitの起動()
実行結果:
B:Hi A、Let's start the conversation
A:ハローB、that's ok
B:How are you
A:I'm fine、and you?
B:I'm fine too
A:Nice to meet you
B:Nice to meet you,too
A:That's all for today
B:Oh、goodbye
Semaphore(信号量)
スレッドの同時数を制御するために、爬虫類中の要求回数が頻繁すぎると、ipが禁止されます。ページに登るスレッドの数を制御するたびに、ipが禁止されるのをある程度防ぐことができます。ファイルの読み書きでは、書き込みスレッドを制御するのは毎回一つで、スレッドを読むのは複数です。

import time
import threading


def get_thread_a(semaphore,i):
 time.sleep(1)
 print("get thread : {}".format(i))
 semaphore.release()


def get_thread_b(semaphore):
 for i in range(10):
  semaphore.acquire()
  thread_a = threading.Thread(target=get_thread_a, args=(semaphore,i))
  thread_a.start()


if __name__ == "__main__":
 semaphore = threading.Semaphore(2)
 thread_b = threading.Thread(target=get_thread_b, args=(semaphore,))
 thread_b.start() 

上記の例では、2つのスレッドを1秒ごとに同時に実行する場合、Semaphore.acquire()を呼び出すと、Semaphoreの数は、Semaphoreの数が0になるまで1減って、release()後にSemaphoreの数が1だけ加算されます。Semaphoreは本質的には呼び出しのCoditionであり、semaphore.acquire()はSemaphoreの値が0の条件でCondition.wait()を呼び出します。そうでないと1を減算します。Semaphore.release()はSemaphoreの値を1を加算してCondation.notify()を呼び出します。
Semaphoreソース

def acquire(self, blocking=True, timeout=None):
  if not blocking and timeout is not None:
   raise ValueError("can't specify timeout for non-blocking acquire")
  rc = False
  endtime = None
  with self._cond:
   while self._value == 0:
    if not blocking:
     break
    if timeout is not None:
     if endtime is None:
      endtime = _time() + timeout
     else:
      timeout = endtime - _time()
      if timeout <= 0:
       break
    self._cond.wait(timeout)
   else:
    self._value -= 1
    rc = True
  return rc

def release(self):
  with self._cond:
   self._value += 1
   self._cond.notify()

以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に対して一定の参考となる学習価値を持っています。質問があれば、メッセージを書いて交流してください。ありがとうございます。