1.queue队列
2.进程间通讯IPC机制
3.什么是线程(理论)
4.开启线程的俩种方式
5.生产者消费者模型
6.线程的其他方法
7.线程间是可以通讯的
8.守护线程
9.互斥锁
一.queue队列
1.队列:先进先出
2.堆栈:先进后出
1 # get和put最好是一一对应,而且不要超过你传入的最大值,不会回进入阻塞态 2 from multiprocessing import Queue 3 4 q = Queue(5)# 括号内可以放参数,表示的是这个队列最大存储量 5 6 q.put('q') 7 q.put('w') 8 q.put('e') 9 q.put('r') 10 q.put('t') 11 q.put('t')# 如果队列被放满了,还继续往队列里面添加值,程序不会报错,会原地等待,直到有人取走值 12 13 14 print(q.get()) 15 print(q.get()) 16 print(q.get()) 17 print(q.get()) 18 print(q.get())# 如果没有值取空了,在取的话,程序会进入阻塞状态,知道有人继续往队列中添加值 19 20 21 ''' 22 还有几种方法(但这几种方法都不适用多进程的情况) 23 q.full()判断队列是否满了 24 print(q.empty())判断队列中的数据数据是否取完了 25 print(q.get_nowait())取值,没有值不等待直接报错 26 '''
二.进程间通讯IPC机制
进程间通讯
通讯指的就是交换数据
进程之间内存是相互隔离的,当一个进程想要把数据给另外一个进程,就需要考虑IPC
用队列可以测验出进程间是可以通过一些方法进行通讯的
from multiprocessing import Process,Queue def qwe(q): q.put('hello ') def zxc(q): print(q.get(q)) if __name__ == '__main__': q = Queue() p = Process(target=qwe,args=(q,)) p1 = Process(target=zxc,args=(q,)) p.start() p1.start()
三.什么是线程
什么是线程: 线程是操作系统最小的运算调度单位,被包含在进程中,一个线程就是一个固定的执行流程 线程和进程的关系: 线程不能单独存在,必须存在于进程中, 进程是一个资源单位,其中包含了运行程序所需要的所有资源 线程才是真正的执行单位,没有线程,进程中的资源无法被利用起来, 所以一个进程至少包含一个线程,称之为主线程,当我们启动一个程序时, 操作系统就会自己为这个程序创建一个主线程,线程也可以由程序后期开启, 自己开启的线程称之为子线程 那位什么需要线程: 开进程: 1 申请内存空间 ,耗资源 2 拷贝代码 ,耗资源 开线程: 目的只有一个就是提高效率,将内存比如工厂,那么进程就相当于是工厂里面的车间, 而你的线程就相当于是车间里面的流水线,要是生产量跟不上的话,你肯定是创建新的流水线, 而不是创建一个新的工厂 一个进程内可以起多个线程,并且线程与线程之间的数据是共享的 ps:开启线程的开销要远远小于开启进程的开销
四.开启线程的俩种方式
创建线程的俩种方式
跟创建进程的方式是一样的
一》
from threading import Thread
import time
def task(name):
print('%s is running'%name)
time.sleep(2)
print('%s is over'%name)
# 注意:开启线程的时候不需要在__main__内
t = Thread(target=task,args=('zy',))
t.start()# 告诉操作系统开一个线程,线程的开销远远小于进程
# 小到代码一执行完,线程就开启了
print('主')
二》
from threading import Thread
import time
class MyThread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
print('%s is running'%self.name)
time.sleep(2)
print('%s is over'%self.name)
t = MyThread('zy')
t.start()
print('主')
五.生产者消费者模型
生产者消费者是什么:
生产者消费者 生产者消费者模型是什么: 模型,就是解决摸个问题的套路, 产生数据的一方称之为生产者, 处理数据的一方称之为消费者, 列如:爬虫,生活中到处都是这种模型, 饭店 厨师即使生产者,你吃饭的人就是消费者 ## 生产者和消费者出啥问题了? 解决什么问题 生产者和消费,处理速度不平衡,一方快一方慢,导致一方需要等待另一方 ## 生产者消费者模型解决这个问题的思路: 怎么解决 原本,双方是耦合 在一起,消费必须等待生产者生成完毕在开始处理, 反过来 如果消费消费速度太慢,生产者必须等待其处理完毕才能开始生成下一个数据 ### 解决的方案: 将双方分开来.一专门负责生成,一方专门负责处理 这样一来数据就不能直接交互了 双方需要一个共同的容器 生产者完成后放入容器,消费者从容器中取出数据 这样就解决了双发能力不平衡的问题,做的快的一方可以继续做,不需要等待另一方
列子:
生产者:生产/制造数据的
消费者:消费/处理数据的
例子:做包子的,买包子的
1.做包子远比买包子的多
2.做包子的远比买包子的少
如何解决供需不平衡的问题
第一种解决方法:
1 第一个版本 2 from multiprocessing import Process,Queue,JoinableQueue 3 import random 4 import time 5 6 7 def zuo(name,food,q): 8 for i in range(10): 9 data = '%s 生产了%s%s'%(name,food,i) 10 time.sleep(random.random()) 11 q.put(data) 12 print(data) 13 14 def chi(name,q): 15 while True: 16 data = q.get() 17 if data == None:break# 判断取到的是否为空 18 print('%s吃了%s'%(name,data)) 19 time.sleep(random.random()) 20 21 if __name__ == '__main__': 22 q = Queue() 23 z = Process(target=zuo,args=('厨师zy','牛排',q)) 24 z1 = Process(target=zuo,args=('厨师xj','鸡排',q)) 25 c = Process(target=chi,args=('人',q)) 26 c1 = Process(target=chi,args=('动物',q)) 27 z.start() 28 z1.start() 29 c.start() 30 c1.start() 31 z.join() 32 z1.join()# 没有这个的话,直接取空,消费者就结束了,确保生产者确确实实生产完了 33 q.put(None) # 存进去一个None,只要消费者取值得时候取到空了,就带表生产的都被吃完了,然后结束程序 34 q.put(None) 35 36 第一种方法的缺陷:没多一个人我就要多写一个None,要是人有成千上万怎么办
第二种方法:用JoinableQueue方法解决
JoinableQueue里面的方法:
JoinableQueue
可以被join的队列
join是等待任务结束
队列怎么叫结束?
调用task_done一次则表示有一个数据被处理完成了 当task_done次数等于put的次数就意味着任务处理完成了
这也是join的执行时机
该队列可以明确告知数据的使用方,所有数据都已经处理完成
在生产者消费者模型中,解决了消费者不知道何时算是任务结束的问题
具体过程:主进程先等待所有的生产者进程生成完毕,再等队列中的数据被全部处理,这就意味着,任务全部结束
1 from multiprocessing import Process,JoinableQueue 2 import random 3 import time 4 5 6 def zuo(name,food,q): 7 for i in range(10): 8 data = '%s 生产了%s%s'%(name,food,i) 9 time.sleep(random.random()) 10 q.put(data) 11 print(data) 12 13 def chi(name,q): 14 while True: 15 data = q.get() 16 print('%s吃了%s'%(name,data)) 17 time.sleep(random.random()) 18 q.task_done()# 告诉队列你已经从队列中取出了一个数据,并且处理完毕 19 20 if __name__ == '__main__': 21 q = JoinableQueue() 22 z = Process(target=zuo,args=('厨师zy','牛排',q)) 23 z1 = Process(target=zuo,args=('厨师xj','鸡排',q)) 24 c = Process(target=chi,args=('人',q)) 25 c1 = Process(target=chi,args=('动物',q)) 26 z.start() 27 z1.start() 28 c.daemon = True# 将消费者设为守护进程,主线程结束,子线程也就结束了 29 c1.daemon = True 30 c.start() 31 c1.start() 32 z.join()# 确保生产者确确实实生产完了 33 z1.join() 34 35 q.join()# 等待队列中数据全部取出
六.线程的其他方法
from threading import Thread,current_thread,active_count import time import os def task(name,i): print('%s is running'%name) # print('子current_thread:',current_thread().name)# 查看子线程 # print('子',os.getpid()) time.sleep(i) print('%s is over'%name) # 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内 t = Thread(target=task,args=('egon',1)) t1 = Thread(target=task,args=('jason',2)) t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程 t1.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程 t1.join() # 主线程等待子线程运行完毕 print('当前正在活跃的线程数',active_count())# 查看正在活跃的线程数 # 小的代码执行完 线程就已经开启了 print('主') # print('主current_thread:',current_thread().name) # print('主',os.getpid())# 查看线程id
七.线程间是可以通讯的
from threading import Thread money = 666 def task(): global money money = 999 t = Thread(target=task) t.start() t.join() print(money) 线程之间是可以通信的,数据之间不是相互隔离的 结果是999
八.守护线程
一个线程可以设置为另一个线程的守护进程
特点:被守护线程结束后守护线程也随之结束
守护线程会等到所有非守护线程结束后结束!前提是除了主线程之外,还有其他的非守护线程
当然如果守护线程已经完成任务,就会立马结束
1 from threading import Thread 2 import time 3 4 def task(): 5 print("子1running......") 6 time.sleep(5) 7 print("子1over......") 8 9 def task2(): 10 print("子2running......") 11 time.sleep(4) 12 print("子2over......") 13 14 t = Thread(target=task) 15 t.daemon = True 16 t.start() 17 18 t2 = Thread(target=task2) 19 t2.start() 20 print("主over")
九.互斥锁
线程互斥锁
不管是进程还是线程,只要共享就意味着竞争,线程中也存在安全问题,
多线程可以并发执行,一旦并发了并且访问了同一个资源就会出现问题,
解决方案:还是要加互斥锁
1 from threading import Thread,Lock 2 import time 3 4 n = 100 5 6 def task(mutex): 7 global n 8 mutex.acquire() 9 tmp = n 10 time.sleep(0.1) 11 n = tmp - 1 12 mutex.release() 13 14 t_list = [] 15 mutex = Lock() 16 for i in range(100): 17 t = Thread(target=task,args=(mutex,)) 18 t.start() 19 t_list.append(t) 20 for t in t_list: 21 t.join() 22 print(n) 23 24 开100个线程,每一次都去减一,最终打印结果为0,如果不加锁的话打印结果为99,因为每个线程拿到的都是100,100减1等于99