Python线程消费者在生产者之前被调用



我写了一个简单的python程序来理解线程。这个程序没有像time.sleep(x)那样的线程阻塞逻辑,但是我仍然不明白消费者线程怎么可能在生产者线程之前被调用。

生产者→向list添加一个数值消费者→获取/打印此列表中的数字值。

消费者怎么能在生产者生成数字之前打印?

import threading
import time
import datetime
from colorama import Fore as colorify

def main() -> None:
data=[]
threads = [
threading.Thread(target=producer,args=(data,5),daemon=True),
threading.Thread(target=consumer,args=(data,5),daemon=True)
]
[t.start() for t in threads]
[t.join() for t in threads]

print(f"{colorify.YELLOW}Execution completed")
def producer(data: list, num:int) -> None:
for i in range(num):
_t = datetime.datetime.now()
sqrt=i*i
data.append([i,_t])
print(f"{colorify.BLUE} producer {i} {threading.get_ident()}")

def consumer(data : list, num: int) -> None:
for i in data:
item= i
number=item[0]
_t=item[1]
print(f"{colorify.RED} consumer {number}  {threading.get_ident()}")

output:
producer 0 18064
producer 1 18064
producer 2 18064
producer 3 18064
consumer 0  8028
consumer 1  8028
consumer 2  8028
consumer 3  8028
producer 4 18064
Execution completed
0.0028671000036410987 seconds

这是一个竞争条件,如果列表更大,其中有更多的元素,你只会打印在第二个线程启动时列表中的元素,这只是列表的一部分。

打印也被缓冲了,所以它不会按照你调用print的顺序发生,实际的打印是在缓冲区满的时候完成的,甚至在写列表和打印之间也可以发生上下文切换,打印缓冲区可以并发地写,所以写缓冲区(包括打印)是一个竞争条件,你的整个代码是一个竞争条件。

您可以使用print的flush=True参数来刷新标准输出,以避免缓冲,但是为了解决访问元素时发生的竞争条件,您需要使用队列,并且您还可以在两个线程之间共享锁,以确保添加到队列和打印不会被上下文切换中断。

import threading
import time
import datetime
from colorama import Fore as colorify
import queue

def main() -> None:
data = queue.Queue()
lock = threading.Lock()
threads = [
threading.Thread(target=producer, args=(data, 5, lock), daemon=True),
threading.Thread(target=consumer, args=(data, 5, lock), daemon=True)
]
[t.start() for t in threads]
[t.join() for t in threads]
print(f"{colorify.YELLOW}Execution completed", flush=True)

def producer(data: queue.Queue, num: int, lock: threading.Lock) -> None:
for i in range(num):
_t = datetime.datetime.now()
sqrt = i * i
with lock:
data.put([i, _t])
print(f"{colorify.BLUE} producer {i} {threading.get_ident()}", flush=True)

def consumer(data: queue.Queue, num: int, lock: threading.Lock) -> None:
for i in range(num):
item = data.get()
number = item[0]
_t = item[1]
with lock:
print(f"{colorify.RED} consumer {number}  {threading.get_ident()}", flush=True)

main()
producer 0 5692
producer 1 5692
producer 2 5692
producer 3 5692
producer 4 5692
consumer 0  12644
consumer 1  12644
consumer 2  12644
consumer 3  12644
consumer 4  12644
Execution completed

注意,这段代码中的flush是不需要的,因为锁会阻止并发写标准输出缓冲区。

最新更新