一、队列的使用
-
队列的认识:
- Python的Queue模块中提供了同步的、线程安全的队列类,包括:FIFO(先入先出)队列QueueLIFO、(后入先出)队列LifoQueue、优先级队列PriorityQueue,这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步
- 队列中数据添加和获取 已经加了锁(所有用队列来存储多线程公用的全局数据,不会出现资源竞争的问题)
-
队列的的类型:
- (先入先出队列) Queue , 快速使用:from queue import Queue
- (后入先出队列)LifoQueue , 快速使用:from queue import LifoQueue
- 优先级队列 PriorityQueue , 快速使用:from queue import PriorityQueue
队列中的元素为元祖类型:(优先级,数据)------- 例如:(99,33333)
-
类型一:(先入先出) Queue队列
顾名思义:先入先出,先进入的数据,先获取出来-
初始化Queue()对象时,若括号中没有指定最大可接收的消息数量,或数量为负值,代表可接受的消息数量没有上限
例如:q=Queue(3) 表示最大可接收数量为3
例如:q=Queue(0) 表示最大可接收数量没有上限# 创建一个队列对象 q = Queue(3) # 最大只能添加三条数据
-
往队列中添加数据的方法:Queue.put(item)
语法: put(self, item, block=True, timeout=None)
参数1 ———》 item: 数据
参数2 ———》 block: 如果队列已满是否等待;
参数3 ———》 timeout: 等待的超时时间,超时后报错# 1. 往队列中添加数据的方法: put q.put('111') q.put('222') q.put('333') # 添加第四条数据 # 参数:block表示是否等待,timeout表示等待时间 q.put('444',block=False,timeout=3) # 如果队列已满,则会进入堵塞状态(等待)
-
获取队列中数据的方法:Queue.get()
语法: get(self, item, block=True, timeout=None)
参数1 ———》 item: 数据
参数2 ———》 block: 如果队列已满是否等待;
参数3 ———》 timeout: 等待的超时时间,超时后报错# 2. 获取队列中数据的方法:get q.get() # ======> 取出来的数据为 111 q.get() # ======> 取出来的数据为 222 q.get() # ======> 取出来的数据为 333 # 添加第四条数据 # 参数:block表示是否等待,timeout表示等待时间 print(q.get(block=False)) # 如果队列为空,则会进入堵塞状态(等待)
-
判断队列是否满了:Queue.full()
语法: 如果队列满了,返回True, 反之False
# 3. full:判断队列是否已满 print(q.full()) # 满的就是True ,反之
-
判断队列是否为空:Queue.empty()
语法: 如果队列为空,返回True, 反之False
# 4. empty:判断队列是否为空 print(q.empty()) # 为空的就是True ,反之
-
返回队列消息数量:Queue.qsize()
语法:返回当前队列包含的消息数量
# 5. 取队列中的数据量 print(q.qsize())
-
Queue.task_done()
语法:在完成一项工作之后,使用Queue.task_done()方法可以向队列发送一个信号,表示该任务执行完毕
-
Queue.join()
- 语法:实际上意味着等到队列中所有的任务(数据)执行完毕之后,再往下,否则一直等待
- 注意点:join()是判断的依据,不单单指的是队列中没有数据,数据get出去之后,要使用task_done()向队列发送一个信号,表示该任务执行(数据使用)完毕
- 队列中task_done方法和join 是相互配合使用的,缺一不可
1.在使用线程实现子线程执行完后在执行主线程
# 初始化对象 q = Queue() for i in range(15): q.put('数据{}'.format(i)) # 添加队列数据 def work(data): """线程执行的工作函数""" for i in range(5): time.sleep(random.randint(1, 4)) # 随机等待时间 print("数据data:{}处理完毕".format(data)) def main(): """使用线程等待的方法""" ts =[] for i in range(10): data = q.get() t1 = Thread(target=work, args=(data,)) t1.start() ts.append(t1) for i in ts: i.join() print("------等待线程中所有的数据处理完毕再往下执行-----------")
2.使用队列来实现子线程执行完后在执行主线程
# 初始化对象 q = Queue() for i in range(15): q.put('数据{}'.format(i)) # 添加队列数据 def work(data): """线程执行的工作函数""" for i in range(5): time.sleep(random.randint(1, 4)) # 随机等待时间 print("数据data:{}处理完毕".format(data)) # 给队列发送一个信号,已经处理好了 q.task_done() def main(): """使用队列等待的方法""" for i in range(10): data = q.get() # 获取数据 t1 = Thread(target=work, args=(data,)) # 设置线程 t1.start() # 启动线程 # 等待队列中所有的任务执行完毕(数据处理完毕) q.join() print("------等待线程中所有的数据处理完毕再往下执行-----------")
-
4.类型二和三:(后入先出) LifoQueue 队列 和 (优先级队列) PriorityQueue
三种类型的队列,Queue队列、 LifoQueue 队列 、PriorityQueue 方法都是一样,唯一区别是数据的的取出不一样
顾名思义:
后入先出,后面进入的数据,先获取出来
优先级队,根据排序优先级,先获取出来
-
初始化对象时,若括号中没有指定最大可接收的消息数量,或数量为负值,代表可接受的消息数量没有上限
例如:q=LifoQueue (3) 表示最大可接收数量为3
例如:q=LifoQueue(0) 表示最大可接收数量没有上限# 创建一个队列对象 q = LifoQueue(3) # 最大只能添加三条数据 q = PriorityQueue() # 最大可接收数量没有上限
-
往队列中添加数据的方法:LifoQueue .put(item) / PriorityQueue.put(item)
语法: put(self, item, block=True, timeout=None)
参数1 ———》 item: 数据
参数2 ———》 block: 如果队列已满是否等待;
参数3 ———》 timeout: 等待的超时时间,超时后报错# LifoQueue 队列 # 1. 往队列中添加数据的方法: put q.put('111') q.put('222') q.put('333') # 添加第四条数据 # 参数:block表示是否等待,timeout表示等待时间 q.put('444',block=False,timeout=3) # 如果队列已满,则会进入堵塞状态(等待) # PriorityQueue队列 # 1. 往队列中添加数据的方法: put q.put((77,'111')) # 队列中的元素为元祖类型) q.put((4,'222')) q.put((888,'333'))
-
获取队列中数据的方法:LifoQueue .get(item) / PriorityQueue.get(item)
语法: get(self, item, block=True, timeout=None)
参数1 ———》 item: 数据
参数2 ———》 block: 如果队列已满是否等待;
参数3 ———》 timeout: 等待的超时时间,超时后报错# LifoQueue 队列 # 2. 获取队列中数据的方法:get q.get() # ======> 取出来的数据为 111 q.get() # ======> 取出来的数据为 222 q.get() # ======> 取出来的数据为 333 # 添加第四条数据 # 参数:block表示是否等待,timeout表示等待时间 print(q.get(block=False)) # 如果队列为空,则会进入堵塞状态(等待) # (优先级队列) PriorityQueue # 3. 获取队列中数据的方法:get ----元祖第一个值通常来表示优先级,用第一个值来比较优先级 q.get() # ======> 取出来的数据为 (4, '222') q.get() # ======> 取出来的数据为 (77, '111') q.get() # ======> 取出来的数据为 (888, '333')
二、进程的使用
一 、进程模块的详细使用
-
什么是进程:
一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元
-
进程的状态
- 工作中,任务数往往大于cpu的核数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,因此导致了有了不同的状态
- 就绪状态:运行的条件都已经满足了,随时可以给操作系统调度执行
- 执行状态:cpu正在执行其功能
- 等待状态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态
-
进程和线程有什么区别
- 从功能来说:
进程是能够能够完成多任务,比如 在一台电脑上能够同时运行多个软件
线程是能够完成多任务,比如 一个QQ中的多个聊天窗口 - 从定义上来说:
一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源,在操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),故对它的调度所付出的开销就会小得多, 能更高效的提高系统内多个程序间并发执行的程度
- 从功能来说:
-
创建进程对象: multiprocessing .Process (target=任务函数)
其中参数target指定线程执行的任务 (函数)
-
进程参数的使用
内置的进程的模块是:multiprocessing 模块里的 Process 类
multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情
快速使用:from multiprocessing import ProcessProcess 类有以下参数:
1、target参数: 指定任务函数
import time from multiprocessing import Process def func1(): for i in range(5): print("-正在做事情1------") time.sleep(1) # 创建一个线程对象 aa = Process(target=func1)
2、name参数: 设置进程名
# 创建一个进程对象 aa = Process(target=func1,name="henry")
3.、args参数: 给任务函数传参(参数传递的是元祖类型)
def func1(aa): for i in range(5): print("---{}---正在做事情1------".format(aa) time.sleep(1) # 创建一个进程对象 aa = Process(target=func1,args=('张三',))
4、kwargs参数:给任务函数传参(参数传递的是字典类型)
def func1(aa): for i in range(5): print("---{}---正在做事情1------".format(aa) time.sleep(1) # 创建一个进程对象 aa = Process(target=func1,kwargs={'aa': "李四"})
5、daemon参数:设置是否作为守护进程(bool值)
- 设置子进程守护主进程执行(主进程结束,守护进程自动结束)
- 如果没有设置守护进程,主进程跑完,子进程没有跑完,还会继续跑下去,不会有影响
- 如果设置了守护进程,主进程跑完,子进程没有跑完,当前进程全部结束(包括子进程,不管是否跑完)
- 如果多个子进程,一个设置了守护进程,一个没有设置守护进程,主进程结束,子进程还会继续跑下去,不会有影响
def func1(aa): for i in range(5): print("---{}---正在做事情1------".format(aa)) # print("---正在做事情1------") time.sleep(1) def func2(aa): for i in range(6): print("----{}--正在做事情2------".format(aa)) time.sleep(1) # 创建一个进程对象 t1 = Process(target=func1, args=('张三',),daemon=True) # 设置守护进程 t2 = Process(target=func2, kwargs={'aa': "李四"},daemon=True) # 设置守护进程 # 启动进程执行 t1.start() t2.start() time.sleep(2) print("----主线程执行结束-----------")
-
进程方法的使用
Process类提供了以下方法:
1、start()方法: 启动进程执行
# 启动线程执行 t1.start() t2.start()
2、join([time]): 设置主进程等待的时间
- 设置主进程会等待time秒后再往下执行,不设置time,则默认为子进程结束,多个子进程之设置的值会叠加
- 多个子进程同时使用join设置的时间会叠加
def func2(): for i in range(6): print("------正在做事情2------") time.sleep(1) # 创建一个进程对象 t1 = Process(target=func2) # 子进程t1 # # 子进程t1启动执行 t1.start() # 主进程等待时间 t1.join() # 让主进程等待t1执行完,再往下执行 # 主进程等待子线程执行完 for i in range(10): print("主进程全部执行完成——————————————————————————")
3、run方法 :进程接收到的任务函数,最终是在run方法里面执行的(进程执行的任务)
4、isDaemon: 判断是否为守护线程
5、isAlive: 判断线程支持存活(处于执行状态)
6、is_alive:判断线程支持存活(处于执行状态)
7、getName:获取线程名
-
注意点
使用多进程时,要确保程序的入口在if name == ‘main’:中,不然会报错
-
多进程的数据通信问题
- 各个进程之间的数据都是独立的,互不相同
- 进程之间进行数据传输,只能使用进程模块中的队列multiprocessing.Queue,使用队列中的Queue 会报错
- 每创建一个进程,会有一个默认的线程
进程是资源分配的基本单位,线程是操作系统任务调动执行的基本单位
-
multiprocessing.Queue() 和 queue.Queue区别
- from multiprocessing import Process, Queue =========》这是进程模块
multiprocess.Queue 是跨进程通信队列使用 - from queue import Queue =========》这是队列模块
queue.Queue是进程内队列,同一个进行中多个线程之间通信用
- from multiprocessing import Process, Queue =========》这是进程模块
例如:
a = 0 def work1(q): a = q.get() for i in range(2000000): a += 1 print("work中的a:", a) q.put(a) def work2(q): a = q.get() for i in range(1000000): a += 1 print("work2中的a:", a) q.put(a) def main(): a = 0 que = Queue() # 创建队列对象 que.put(a) p1 = Process(target=work1, args=(que,)) # 进程1 p1.start() p2 = Process(target=work2, args=(que,)) # 进程1 p2.start() p1.join() p2.join() a = que.get() print("主进程中a:", a) if __name__ == '__main__': main()