我写了一个简单的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
是不需要的,因为锁会阻止并发写标准输出缓冲区。