虽然python中的循环在包含锁

时间:2018-03-31 15:56:20

标签: python multithreading queue locking

我目前正在学习如何在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")的原因,我注意到只有一个线程会终止。换句话说,当我运行上面的代码时,只打印一个线程"下一行是返回"。

奇怪的是,当我移除锁时,这个问题就消失了。没有锁,它完全正常。

我错过了什么?

1 个答案:

答案 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