2017-05-27 78 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它并不能保证后续调用get()不会阻止。

问题是,另一个线程可能会在调用empty()和调用get()之间运行,从而窃取empty()方法,否则报告为存在。本教程可能会使用该锁来确保线程可以独占访问从调用到empty()的队列,直到调用get()。如果没有这个锁,两个线程可能会进入if语句,并向get()发出一个调用,这意味着其中一个线程可能会阻塞,等待一个永远不会被推送的项目。


让我告诉你如何正确使用线程安全队列。而不是检查空的()首先,只需直接依赖GET)的阻塞行为(:

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

队列的内部锁定将确保两个线程不干扰呼叫的持续时间来获得()并且不需要queueLock。请注意,本教程的原始代码会每隔1秒周期检查一次exitFlag,而修改后的队列要求您在将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(),然后调用Queue.join()在主线程中。在process_data()的循环体的末尾,调用q.task_done()。在主控制器代码中,而不是上述的while循环,请致电q.join()

请参阅队列模块上Python文档页底部的example


或者,你可以保持queueLock与一个普通的老名单更换线程队列如下:

  • 更换workQueue = queue.Queue(10)workQueue = []
  • if len(workQueue) > 0
  • 更换if not workQueue.empty()替换为workQueue.get()workQueue.pop(0)
  • 替换workQueue.put(word)workQueue.append(word)

请注意,这不会保留put()存在于原始版本中的阻塞行为。

+0

非常感谢。另一个问题,例如当线程获取exitFlag为1时,它肯定会终止,我想当代码运行到最后一部分以在那些线程上调用join()时,它们应该都已经终止,那么调用的目的是什么? join()对他们? –

相关问题