我需要锁来保护我的代码中的多线程竞争条件

时间:2016-09-04 06:08:54

标签: python multithreading python-2.7 jython

在Windows上使用Python 2.7,并将使用支持真正多线程的Jython。方法sendMessage用于接收来自特定客户端的消息,客户端可以将相同的消息发送给其他几个客户端(参数receivers的用途是什么,receivers是一个列表)。方法receiveMessage用于接收从其他客户端发送的特定客户端的消息。

问题是我是否需要方法sendMessagereceiveMessage的任何锁定?我认为没有必要,因为即使客户端X正在接收其消息,对于另一个客户端Y附加到消息池以向客户端X传递消息也是完美的。我认为对于defaultdict / list,append / pop是无论是原子还是无需保护。

如果我错了,请随时纠正我。

from collections import defaultdict

class Foo:
    def __init__(self):
        # key: receiver client ID, value: message
        self.messagePool = defaultdict(list)
    def sendMessage(self, receivers, message):
        # check valid for receivers
        for r in receivers:
            self.messagePool[r].append(message)
    def receiveMessage(self, clientID):
        result = []
        while len(self.messagePool[clientID]) > 0:
            result.append(self.messagePool[clientID].pop(0))

        return result

3 个答案:

答案 0 :(得分:4)

我建议使用Queue代替list。它被设计用于在带有锁定的线程中附加\ pop。

答案 1 :(得分:3)

我认为这个问题已经为CPython herehere得到了很好的回答(基本上,你因为GIL而安全,但文档中没有任何内容(例如defaultdict)或list)正式谈到这一点)。但我理解你对Jython的关注,所以让我们使用一些官方来源解决它,比如Jython source code。 pythonic listjavaish PyList,有这种代码:

public void append(PyObject o) {
    list_append(o);
}
final synchronized void list_append(PyObject o) {
...
}
public PyObject pop(int n) {
    return list_pop(n);
}
final synchronized PyObject list_pop(int n) {
...
}

由于我们有这些方法synchronized,我们可以确定列表追加和弹出对Jython也是线程安全的。因此,您的代码似乎是安全的线程。

虽然Queue建议仍然有效,但这个用例确实更合适。

答案 2 :(得分:2)

竞争条件是两个或多个线程同时改变某些全局状态。

sendMessage的代码中,您正在更改self.messagePool[r],这是一个全局对象。因此,在添加新项目之前应锁定self.messagePool[r]

与您的receiveMessage功能相同。

list.appendlist.pop是操作O(1)和O(1)操作,所以它很少会导致任何竞争条件。但是,风险仍然存在。