我正在用Python的Queue.Queue
类实现一个相对简单的线程池。我有一个包含Queue
实例的生成器类以及一些便捷方法,以及一个子类threading.Thread
的使用者类。我根据整数为我想要的每个线程(“工作线程”,我认为它们被调用)实例化该对象。
每个工作线程从队列中取出flag, data
,使用自己的数据库连接处理它,并将行的GUID放到列表中,以便生产者类知道作业何时完成。
虽然我知道其他模块实现了我正在编码的功能,但我编码的原因是为了更好地理解Python线程的工作原理。这让我想到了我的问题。
如果我将任何东西存储在函数的命名空间或类的__dict__
对象中,它是否是线程安全的?
class Consumer(threading.Thread):
def __init__(self, producer, db_filename):
self.producer = producer
self.conn = sqlite3.connect(db_filename) # Is this var thread safe?
def run(self):
flag, data = self.producer.queue.get()
while flag != 'stop':
# Do stuff with data; Is `data` thread safe?
我认为两者都是线程安全的,这是我的理由:
__dict__
。在上面概述的场景中,我认为任何其他对象都不会引用此对象。 (现在,如果我使用join()
功能,情况可能会变得更复杂,但我不是......)global
,所以我不明白其他任何对象如何引用函数变量。This post在某种程度上解决了我的问题,但对我来说仍然有点抽象。
提前感谢您为我清理这件事。
答案 0 :(得分:5)
这里唯一的“风险”是数据对象的值:理论上,生产者可能在将数据对象放入队列后保留在数据对象上,并且(如果数据对象本身是可变的 - 请确保您理解“可变”意味着可以在消费者使用它时改变对象。如果生产者在将数据对象放入队列后单独留下数据对象,则这是线程安全的。
答案 1 :(得分:2)
要使数据线程安全,请使用copy.deepcopy()在将数据放入队列之前创建新的数据副本。然后,生产者可以修改下一个循环中的数据,而无需在消费者副本到达之前对其进行修改。
答案 2 :(得分:1)
我认为你的假设完全正确,在你的情况下,你很可能是正确的。
然而,你说的话,稍微更难判断某些东西是否是线程安全的。
self.conn = sqlite3.connect(db_filename)
之类的调用可能不是,因为sqlite3模块可能共享一些状态并且调用该函数可能会产生一些副作用。但是,我怀疑是这种情况和我一样,我认为它会产生一个全新的变量。
不仅仅是全局变量可能是一个问题,从外部范围获取可变变量也是一个问题。
所以
中的数据flag, data = self.producer.queue.get()
可能是也可能不是线程安全的,具体取决于最初生成数据的位置。但是,我认为这些数据将包含独立的(最好是不可变的)信息。所以,如果是这样,那么所有都应该是线程安全的。