12.2.3 线程同步
如果有多个线程共同修改或者操作同一对象或数据,就会可能发生一些意想不到的事 如:
import time
import threading
class MyThread(threading.Thread):
def __init__(self,thread_id):
super(MyThread,self).__init__()
self.thread_id = thread_id
def run(self):
for i in range(10):
print("Thread %d\t printing! times:%d" % (self.thread_id,i))
time.sleep(1)
for i in range(10):
print("Thread %d\t printing! times: %d" % (self.thread_id,i))
def main():
print("Main thread start")
threads = []
#创建线程
for i in range(5):
thread = MyThread(i)
threads.append(thread)
#启动线程
for i in range(5):
threads [i].start()
#等待进程执行完毕
for i in range(5):
threads [i].join()
print("Main thread finish")
if __name__ == "__main__":
main()
这个例子每运行一次他的输出结果都是不同的。为了保证数据的正确性,我们需要将多个线程进行同步。
标准库中threading中有个Lock对象可以实现简单的线程同步
12.2.4 队列
标准库提供了一个非常有用的Queue模块、可以帮助我们自动的控制锁,保持数据同步。
Python 的 Queue 模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。
这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。
Queue 模块中的常用方法:
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作
from queue import Queue
q = Queue()
for i in range(5):
q.put(i)
while not q.empty():
print(q.get())
Queue模块并不是一定要使用多线程才能使用,这个例子使用单线程演示了元素以插入顺序从队列中移除
import time
import threading
import queue
#创建工作队列并且限制队列的最大元素是10个
work_queue = queue.Queue(maxsize=10)
#创建结果队列并且限制队列的最大元素是10个
result_queue = queue.Queue(maxsize=10)
class WorkerThredd(threading.Thread):
def __init__(self,thread_id):
super(WorkerThredd,self).__init__()
self.thread_id = thread_id
def run(self):
while not work_queue.empty():
#从工作队列获取数据
work = work_queue.get()
#模拟工作耗时3秒
time.sleep(3)
out = "Thread %d\t received %s" %(self.thread_id,work)
#把队列放入结果队列
result_queue.put(out)
def main():
#把工作队列放入数据
for i in range(10):
work_queue.put("message id %d" %i)
#开启两个工作线程
for i in range(2):
thread = WorkerThredd(i)
thread.start()
#输出十个结果
for i in range(10):
result = result_queue.get()
print(result)
if __name__ == '__main__':
main()
多线程使用Queue模块也不需要多余的锁操作,因为queue.Queue对象已经在执行方法的时候帮助我们自动调用threading.Lock来实现锁的使用,标准库queue模块不止有Queue一种队列,还有LifoQueue和PriorityQueue等功能更复杂的队列。