关于多线程:python的queue.Queue.put()方法是异步的吗?

Is python's queue.Queue.put() method asynchronous?

本问题已经有最佳答案,请猛点这里访问。

如果我作为工作线程运行具有以下函数的线程,

1
2
3
4
5
6
7
8
q = queue.Queue()

def worker():
    while True:
        t = {}
        for i in range(3):
            t['a'] = i
            q.put(t)

队列中填充了所有相同的字典,即{'a': 2},而不是顺序{'a': 0}, {'a': 1}, {'a': 2}。我认为这是因为put()方法在for循环完成后运行,i的最后一个值为2。我解释得对吗?

现在,如果我在for循环中移动字典的实例化,

1
2
3
4
5
def worker():
    while True:
        for i in range(3):
            t = {'a': i}
            q.put(t)

队列中填充了所需的序列。我的解释是,在第一个实例中,我在内存中创建一个dictionary对象,然后开始一个for循环,并重新分配它的值3次,但是put()调用在循环完成后发生。在第二个实例中,我在for循环的每次迭代中都创建一个新的dictionary对象,因此当put()调用在循环之后发生时,它们使用自己的键值对访问字典的3个不同实例。

有人能告诉我窗帘后面发生了什么吗?


我解释得对吗?

你观察这种行为是因为你一直在修改同一个对象

让我们把队列/线程放在一边,用一些prints运行一个简化的代码等价物来了解正在发生的事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
t = {}
l = []
for i in range(3):
    t['a'] = i
    l.append(t)
print(l)
t['a'] = 20
print(l)
print(map(id, l))

[{'a': 2}, {'a': 2}, {'a': 2}]
[{'a': 20}, {'a': 20}, {'a': 20}]
# they are all the same!
[4474861840, 4474861840, 4474861840]

所以它与线程/队列无关——您只需要添加同一个对象3次。

现在,如果我在for循环中移动字典的实例化

在这种情况下,每次都创建一个新的对象,如下面的代码中所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
l = []
for i in range(3):
    t = {}
    t['a'] = i
    l.append(t)
print(l)
t['a'] = 20
print(l)
print(map(id, l))

[{'a': 0}, {'a': 1}, {'a': 2}]
[{'a': 0}, {'a': 1}, {'a': 20}]
# they are all different!
[4533475600, 4533502592, 4533502872]

所以这里没有魔法

回到你的问题上来

这就是您可能感兴趣的地方:"python的queue.queue.put()线程安全吗?"这意味着多个并发线程可以安全地访问全局变量q。答案是肯定的-它是线程安全的

The Queue module implements multi-producer, multi-consumer queues. It
is especially useful in threaded programming when information must be
exchanged safely between multiple threads. The Queue class in this
module implements all the required locking semantics


在第一个示例中,您将把同一个dict放入队列三次。这与队列无关。您会发现list.append有相同的行为。