我目前正在学习如何在Python中使用线程,而且我正在玩这个用于练习的虚拟代码:
import threading
import queue
import time
my_queue = queue.Queue()
lock = threading.Lock()
for i in range(5):
my_queue.put(i)
def something_useful(CPU_number):
while not my_queue.empty():
lock.acquire()
print("\n CPU_C " + str(CPU_number) + ": " + str(my_queue.get()))
lock.release()
print("\n CPU_C " + str(CPU_number) + ": the next line is the return")
return
number_of_threads = 8
practice_threads = []
for i in range(number_of_threads):
thread = threading.Thread(target=something_useful, args=(i, ))
practice_threads.append(thread)
thread.start()
所有这一切都是创建一个包含5个项目的队列,然后将它们拉出并用不同的线程打印出来。
但我注意到,有些线程没有正确终止。例如,如果我稍后在队列中添加一些内容(例如my_queue.put(7)
),那么某个线程将立即打印该数字。
这就是我添加最后一个打印行print("\n CPU_C " + str(CPU_number) + ": the next line is the return")
的原因,我注意到只有一个线程会终止。换句话说,当我运行上面的代码时,只打印一个线程"下一行是返回"。
奇怪的是,当我移除锁时,这个问题就消失了。没有锁,它完全正常。
我错过了什么?
答案 0 :(得分:2)
实际上,不只是1个线程会给the next line is the return
。可以有1到8之间的任何地方。
在我的处决中,有时我得到1,3,4,5,6,7或1,2,3,4,5,6,7或1,4,5,6,7或仅有5,6 ,7等。
你有竞争条件。
竞争条件介于while
支票not my_queue.empty()
和lock.acquire()
基本上,.empty()
可能会给你一个“它不是空的”,但在你获得锁定之前,其他东西可能会把这个价值拿出来。因此,你需要在锁内对这些东西进行检查。
这是一个更安全的实施:
import threading
import queue
import time
my_queue = queue.Queue()
lock = threading.Lock()
for i in range(50):
my_queue.put(i)
def something_useful(CPU_number):
while True:
lock.acquire()
if not my_queue.empty():
print("CPU_C " + str(CPU_number) + ": " + str(my_queue.get()))
lock.release()
else:
lock.release()
break
print("CPU_C " + str(CPU_number) + ": the next line is the return")
return
number_of_threads = 8
practice_threads = []
for i in range(number_of_threads):
thread = threading.Thread(target=something_useful, args=(i, ))
practice_threads.append(thread)
thread.start()
注意:在您是当前代码中,因为您只获取值 - 它始终是一个阻止程序,即整个循环一次只有一个线程。理想情况下你会这样做:
if not my_queue.empty():
val = my_queue.get()
lock.release()
print("CPU_C " + str(CPU_number) + ": " + str(val))
heavy_processing(val) # While this is going on another thread can read the next val