2017-05-27 3 views
0

私は、python3でマルチスレッドとキューに関するチュートリアルを完了しました。 official tutorialは、 "このモジュールのQueueクラスはすべての必要なロックセマンティクスを実装しています"。しかしanother tutorialで、私は次のように例を見てきました:キューに入れたり出たりするときにキューをロックするのはどうですか?

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 

# Create new threads 
for tName in threadList: 
    thread = myThread(threadID, tName, workQueue) 
    thread.start() 
    threads.append(thread) 
    threadID += 1 

# Fill the queue 
queueLock.acquire() 
for word in nameList: 
    workQueue.put(word) 
queueLock.release() 

# Wait for queue to empty 
while not workQueue.empty(): 
    pass 

# Notify threads it's time to exit 
exitFlag = 1 

# Wait for all threads to complete 
for t in threads: 
    t.join() 
print ("Exiting Main Thread") 
+0

些細な答えは公式のドキュメント> tutorialspointです。 – pvg

答えて

1

私は、あなたがフォローしているチュートリアルでは、Pythonのスレッドセーフキューを使用する方法の悪い例であると考えています。特に、このチュートリアルでは残念なことに余分なロックが必要な方法でスレッドセーフキューを使用しています。実際、この余分なロックは、チュートリアルのスレッドセーフキューを、シンプルリストに基づいて旧式の非スレッドセーフキューに置き換えることができることを意味します。

ロックが必要であることを理由はQueue.empty()のドキュメントによってで示唆されています

空の場合は、()は()を取得するための後続の呼び出しがブロックされないことを保証するものではありませんFalse返します。

問題は、空の()がそうでない場合は存在することが報告というアイテムを盗んで、()別のスレッドが(空にするための呼び出しの間で、実行できる)、コールが取得するということです。このチュートリアルでは、おそらくlockを使ってget()の呼び出しまでスレッドがempty()の呼び出しからキューに排他的にアクセスできるようにしています。このロックがなければ、2つのスレッドがifステートメントに入り、両方がget()の呼び出しを発行します。つまり、それらの1つがブロックされ、決してプッシュされないアイテムを待つことを意味します。


スレッドセーフキューを正しく使用する方法を説明しましょう。代わりに、空のチェックの()最初だけ)(GETのブロッキング動作に直接依存している:

def process_data(threadName, q): 
    while True: 
     data = q.get() 
     if exitFlag: 
      break 
     print("%s processing %s" % (threadName, data)) 

キューの内部ロックは、2つのスレッドが(取得するための呼び出しの間に干渉しないことを保証します)、 queueLockは必要ありません。チュートリアルのオリジナルコードはexitFlagを定期的に1秒ごとにチェックしますが、この修正されたキューでは、exitFlagをTrueに設定した後にダミーオブジェクトをキューにプッシュする必要があります。次のように

コントローラのコードの最後の部分を変更する必要があります:

# Notify threads it's time to exit 
exitFlag = 1 
for _ in range(len(threadList)): 
    # Push a dummy element causing a single thread to wake-up and stop. 
    workQueue.put(None) 
# Wait for all threads to exit 
for t in threads: 
    t.join() 

は、スレッドセーフキューのチュートリアルの使用と別の問題があり、すなわちビジーループが使用されていること

# Wait for queue to empty 
while not workQueue.empty(): 
    pass 

を、スレッドでQueue.task_done()を使用する方がよいと、その後を呼ぶだろう空にするキューを待つために:キューが空にするのを待つメインスレッドでメインスレッドの。 process_data()のループ本体の最後に、q.task_done()を呼び出します。メインコントローラのコードでは、上記のwhileループの代わりにq.join()を呼び出します。

キューモジュールのPythonのドキュメントページの一番下にあるexampleも参照してください。


また、あなたがqueueLockを維持し、次のように昔ながらのリストでスレッドセーフキューを置き換えることができます。

  • workQueue.get()を交換しif len(workQueue) > 0
  • if not workQueue.empty()を交換しworkQueue = []
  • workQueue = queue.Queue(10)を交換してくださいworkQueue.pop(0)
  • workQueue.put(word)workQueue.append(word)

これは、元のバージョンに存在するput()のブロック動作を保持しないことに注意してください。

+0

ありがとうございました。スレッドがexitFlagを1として取得したときに終了することがあるような別の質問です。最後の部分にコードが実行されてそのスレッドのjoin()が呼び出されると、それらはすべて終了しているはずですそれらのjoin()は何ですか? –

関連する問題