我有一个持有字典的课程
class OrderBook:
orders = {'Restaurant1': None,
'Restaurant2': None,
'Restaurant3': None,
'Restaurant4': None}
@staticmethod
def addOrder(restaurant_name, orders):
OrderBook.orders[restaurant_name] = orders
我正在运行调用方法OrderBook.addOrder
的4个线程(每个餐厅一个)。这是每个线程运行的函数:
def addOrders(restaurant_name):
#creates orders
...
OrderBook.addOrder(restaurant_name, orders)
这是安全的,还是在调用addOrder
之前我必须使用锁?
答案 0 :(得分:74)
Python的内置结构对于单个操作是线程安全的,但有时很难看到语句真正成为多个操作的位置。
您的代码应该是安全的。请记住:这里的锁定几乎不会增加任何开销,让您高枕无忧。
http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm有更多详情。
答案 1 :(得分:25)
是的,内置类型本质上是线程安全的: http://docs.python.org/glossary.html#term-global-interpreter-lock
这通过使对象模型(包括关键的内置类型,如dict )隐式安全地防止并发访问,简化了CPython实现。
答案 2 :(得分:1)
值得注意的是,Google的风格指南建议不要依赖于dict原子性,正如我在Is Python variable assignment atomic?
中进一步详细解释的那样不要依赖内置类型的原子性。
虽然Python的内置数据类型(例如字典)似乎具有原子操作,但在某些极端情况下它们不是原子操作(例如,如果
__hash__
或__eq__
被实现为Python方法)和不应该依赖它们的原子性。您也不应该依赖于原子变量赋值(因为这又取决于字典)。使用
Queue
模块的Queue数据类型作为在线程之间通信数据的首选方法。否则,请使用线程模块及其锁定原语。了解如何正确使用条件变量,以便您可以使用threading.Condition
而不是使用较低级别的锁。
我同意这一观点:CPython中已经存在GIL,因此使用Lock的性能影响可以忽略不计。当这些CPython实现细节有一天改变时,花在复杂代码库中的错误查找所花费的时间将大大增加。
答案 3 :(得分:0)
当使用 python 的内置 dict 时,set 和 get 是原子的(因为 cpython 的 GIL)。然而,这似乎是不好的做法,因为 .items 之类的操作不是原子的。
注意 - 如果多个线程处理相同的字典键,get-add-set 操作不是线程安全的。