5

好的,代码非常基本。由于我使用多个线程,并且我希望它们之间共享变量,因此我使用的是全局变量。

为什么当我点击“C”时,ThreadClass 中的代码有时不执行?我知道这是一个并发问题,但我不知道如何解决它。我最近阅读了信号量和锁定,但目前我不确定如何实现它。欢迎任何建议。

import threading
buff_list = []

class ThreadClass(threading.Thread):
    global buff_list
    def run(self):
        while (True):
            if ("C" == raw_input()):
                buff_list.append("C")
                print buff_list

class ThreadClass2(threading.Thread):
    global buff_list
    def run(self):
        while(True):
            if ("B" == raw_input() and len(buff_list) > 0):
                buff_list.pop()
                print buff_list

a = ThreadClass()
b = ThreadClass2()

a.start()
b.start()
4

3 回答 3

6

这里有两个同步问题。

让我们首先处理一个更简单的问题,即您正在共享一个buff_list两个线程争夺的全局。没有什么可以阻止一个线程同时尝试append另一个线程pop,这是非法的。而且,即使你很幸运并且没有发生这种情况,也pop可能出现append.


解决这个问题的最简单方法是使用 a Queue,它是自动同步的:

buff_list = Queue.Queue()

然后只需使用put而不是append,而get不是pop


但是,如果您想自己学习这些东西,有两种可能的方法。

首先,您可以使用Lock. (您也可以使用RLock,但我们暂时忘记它。)这样可以确保一次只有一个线程在访问buff_list

buff_lock = threading.Lock()
buff_list = []

现在,无论何时追加或弹出,只需抓住锁:

with buff_lock:
    buff_list.append("C")

with buff_lock:
    val = buff_list.pop()

但这并不能确保弹出代码等到有东西弹出。如果你想这样做,请使用Condition

buff_cond = threading.Condition()

现在:

with buff_cond:
    buff_list.append("C")
    buff_cond.notify()

with buff_cond:
    while not buff_list:
        buff_cond.wait()
    value = buff_list.pop()

第二个问题是你隐式共享sys.stdin,因为两个线程都在调用raw_input。除非您有某种方式来同步事物,以便每个线程都知道它应该何时获得下一个输入(这可能甚至很难描述,如果您无法描述它,您就无法将其转换为代码),那不可能工作——每次你输入C错误的线程都有 50/50 的机会得到它。

因此,正如 kirelagin 建议的那样,您需要让一个线程专门负责 I/O。最简单的方法是再次使用 a Queue,并让一个线程put输入它不使用的任何输入,而另一个线程可以get来自队列。

于 2013-06-03T21:16:01.390 回答
4

好吧,您永远不知道哪个类的实例得到了您的输入。如果您点击“C”并且正在ThreadClass2读取您的输入,那么它将什么也不做,因为"B" == raw_input()will be False

应该只有一个线程负责 I/O。

于 2013-06-03T21:12:28.570 回答
0

除了前面的回复中所说的之外,我想补充一点,线程模块还支持在所有其他语言中也实现的更原始的机制,例如Semaphore Objects

这是计算机科学史上最古老的同步原语之一,由早期荷兰计算机科学家 Edsger W. Dijkstra 发明(他使用 P() 和 V() 代替了 acquire() 和 release())。

我认为,深入学习线程的最佳方法是从头开始。

于 2013-06-03T21:44:16.720 回答