线程间通信主要用到了Event()方法,我对其感性认知类似于锁。当一个线程里event调用等待方法wait()时,该线程即被堵塞,需要另一个线程使用event的set()方法,线程阻塞消失。这里还涉及到队列queue的一些使用,在代码里进行解释。
import threading import logging from queue import Queue import time def get_logger(): logger = logging.getLogger("threading_eg") logger.setLevel(logging.DEBUG) fh = logging.FileHandler("C:\\Users\Administrator\Desktop\\test\\threading3.log") fmt = '%(asctime)s - %(name)s - %(threadName)s - %(message)s' formatter = logging.Formatter(fmt) fh.setFormatter(formatter) logger.addHandler(fh) return logger def creator(data, q, logger): """ 生成用于消费的数据,等待消费者完成处理 """ logger.debug('Creating data and putting it on the queue') for item in data: evt = threading.Event() q.put((item, evt)) logger.debug('Waiting for data to be doubled') evt.wait() def my_consumer(q, logger): """ 消费部分数据,并做处理 这里所做的只是将输入翻一倍 """ while True: data, evt = q.get() logger.debug('data found to be processed: {}'.format(data)) processed = data * 2 logger.debug(processed) time.sleep(5) evt.set() time.sleep(2) if q.empty(): break if __name__ == '__main__': logger = get_logger() q = Queue() data = [5, 10, 13, -1] thread_one = threading.Thread(target=creator, args=(data, q, logger)) thread_two = threading.Thread(target=my_consumer, args=(q, logger)) thread_one.start() thread_two.start()
第一个函数为日志记录函数,不在解释。
第二个函数为生产者函数,即通过传入的列表,将列表里的数据传入队列queue中。
第三个函数为消费者函数,即将列表里的数据取出并翻倍再输出。
主线程里构造了日志对象和队列,创建了一个列表和两个线程,并启动线程。
现在理一理程序的执行逻辑:
当两个子线程启动后,线程1调用生产者函数,遍历列表。当取出第一个数据‘5’时,同时创建了一个Event对象,将Event与第一个数据‘5’打包放进队列,然后event调用wait方法,此时线程1阻塞。
线程2处于不断运行中,只有当队列为空时才退出该线程。由于线程1在线程2前启动,所以此时队列已经有了一个数据。
线程2取出队列里的数据,翻倍,强制等待了5s,后调用event.set()方法,此时线程1不再阻塞,立刻又将数据塞入队列中,此刻强制等待2s,由于线程1在2s前又塞入了一个数据,所以此时队列不为空,继续执行。
直到线程1将所有数据遍历完,队列里没有数据后,线程2同时结束,整个程序结束。
这里的结束程序本应该使用队列的take_done方法和join方法,但是我研究了一会都发现无法结束程序,最后自行写了while判断队列是否为空来结束程序。