线程
!!!不要高估自己!!!
昨日内容回顾:
进程理论
操作系统的调度算法
时间片轮转法
多级反馈队列
同步异步:任务的提交方式
同步:提交任务后原地等待任务的返回结果,才能进行下一步操作
异步:提交任务后不等待任务的返回结果,直接执行下一步操作
阻塞非阻塞:程序的执行状态
阻塞:阻塞态
非阻塞:运行或就绪态
需要掌握的是程序运行的三状态图
进程:正在运行的程序
多道技术:
空间上的复用
多个程序公用一套硬件设备
时间上的复用
CPU的来回切换:切换+保存状态
--程序占用CPU时间过长,会被操作系统强行剥夺走CPU执行权限
--程序接入io操作
input
网络请求 recv accept
time.sleep
文件读写
print
创建进程的两种方式(本质:在内存中申请一块独立的内存空间!!!)
windows下创建进程内部实现:以模块导入的方式,将代码copy到新申请的内存空间中
1.自己定义一个函数,调用Process传参(target=’自己写的函数名‘,args=(v1,))
2.写一个类,继承Process,在里面固定写一个run方法
创建进程的开销比较大
1.申请内存空间
2.拷贝代码
join方法
让主进程等待子进程程序运行结束,不会影响子进程的执行过程!
会将并发变成串行
进程间数据是相互隔离的
如何验证:开一个子进程,修改主进程里面的数据,发现根本无法修改
进程对象的其他方法
查看进程id号
current_process().pid >>Process().pid
os.getpid()
terminate() 主动发起杀死进程的操作
time.sleep(0.1)
is_alive() 判断进程是否存活
僵尸进程和孤儿进程
子进程结束后不会立即释放pid等资源信息
主进程释放子进程资源的两种情况:
主进程正常死亡
join方法
任何进程都会步入僵尸进程,当主进程不停的创建子进程的时候会有害
孤儿进程:主进程意外死亡
在Linux中有一个init,来帮我们回收孤儿进程资源
守护进程
主进程一旦运行完毕,子进程立即结束运行(唇亡齿寒)
互斥锁
为了保证多个程序操作同一份数据数据安全,而设置的一把锁(四人抢厕所)
如何创建锁:
from multiprocessing import Lock
mutex=Lock()
#抢锁
mutex.acquire()
#释放锁
mutex.release()
会将并发变成串行,虽然牺牲了效率,但是提高了数据的安全性
#注意:不要随意的加锁,只会在操作数据的部分加锁
模拟抢票
今日内容:
进程间通信IPC机制
队列:先进先出
堆栈:先进后出
本来进程之间是完全无法通信的,但是可以通过认为的干预,让他们实现通信
multiprocessing 中有一个组件Queue,就可以实现两个进程之间的通信
q=Queue()#一个实例化对象
q.put()#不写参数默认接收好多个
q.put(5)#接收5个数据
q.put()#多放一个会出现阻塞,会一直在原地等待
q.full()#判断队列是否满了!
q.get()#取数据,get一次就是取一次
q.get_nowait()#如果超过了存放的数据,会报错queue.empty
q.get()#如果超过了存放的数据,会一直在原地等待数据传过来,会出现阻塞
q.empty()#判断进程是否为空,但是在并发的情况下这个判断的并不准确
可能一个数据刚产生就被拿走,判断为空,但是下一秒又会满
#基于队列实现线程间通信
from multiprocessing import Process
def producer(q):
q.put('hello')
def constumer(q):
print(q.get())
if __name__=='__main__':
q=Queue()
p=Process(target=producer,args=(q,))
c=Process(target=constumer,args(q,))
p.start()
c.start()
#队列:先进先出
#堆栈:先进后出
#基于队列实现进程间通信
from multiprocessing import Queue
q=Queue(5) #产生一个队列 就相当于产生了一个容器 ()不写参数,可以放好多个
# a=Queue(5) #产生一个最多能够存放五个的数据的队列
# q.put(1)
# q.put(2)
# q.put(3)
# print(q.full())
# q.put(4)
# q.put(5)
# # q.put(6)#往队列中存放数据,如果存放的数据个数大于队列最大存储个数,程序会阻塞
# print(q.full()) #判断队列是否满了?
# for i in range(6):
# q.put(i) #也夯住了
#存取数据,侧重点是取!!
for i in range(5):
q.put(i) #for循环往队列里面存放数据
print(q.get())#取数据,get一次就是取一次
print(q.get())
print(q.get())
q.get_nowait() #在队列有数据的情况下和get取值一样,当队列没有数据的情况下,取值直接报错
print(q.empty()) #判断队列是否为空,需要注意的是在并发的情况下这个方法不准确
print(q.get())
print(q.get())
print(q.empty())
# q.get_nowait() #queue.Empty
# print(q.get())#如果队列为空,get会在原地等待队列中数据传过来
基于队列实现进程间通信
from multiprocessing import Process,Queue
def producer(q):
q.put('hello baby')
def consumer(q):
print(q.get())
if __name__ == '__main__':
q=Queue() #生成一个队列对象
p1=Process(target=producer,args=(q,))
c1=Process(target=consumer,args=(q,))
p1.start()
c1.start()
# print(q.get())
生产者消费者模型****
字面意思:
生产者:火锅店老板
消费者:吃火锅的人
没有任何信息交互的情况下:火锅店老板不知道要买多少菜才能解决今日的客户需求,只能自己估算
吃火锅的人也不知道今天火锅店有没有火锅可以吃,处于一个信息不互通的情况下
就会造成:吃火锅的人多,但是准备的菜少
准备的菜多,但是吃火锅的人少
为了解决这个问题,可以提供预约服务
程序中:
生产者:产生数据的
消费者:处理数据的
解决供需不平衡的问题:
可以将数据放在管道中,专门用来存放固定数量的数据
解决生产者和消费者不需要直接打交道,两者都通过队列实现数据传输
Queue:管道+锁
from multiprocessing import Queue,Process
import time,random
def producer(name,food,q):
for i in range(10):
data=print('%s 生产%s %s'%(name,food,i))
time.sleep(random.randint(1,3))
print(data)
q.put(data)
def constumer(name,q):
while True:
data=q.get()
time.sleep(random.randint(1,3))
print('%s 吃了%s'%(name,data))
if __name__=='__main__':
q=Queue()
p1=Process(target=producer,args=('egon','baozi',q))
p2=Process(target=producer,args=('bob','rou',q))
c1=Process(target=constumer,args=('bob',q))
c2=Process(target=constumer,args=('bob',q))
p1.start()
p2.start()
c1.start()
c2.start()
'''
生产者消费者模型(爬虫)
生产者:做包子的 生产数据的
消费者:买包子的 处理数据的
解决供需不平衡的问题(做包子的做多了,买包子的人多了)
定义一个队列,用来存放固定数量的数据
解决生产者和消费者不需直接打交道,两者都通过队列实现数据传输
Queue:管道+锁
from multiprocessing import Queue,Process
import time,random
def producer(name,food,q):
for i in range(10):
data='%s生产了%s%s'%(name,food,i)
time.sleep(random.randint(1,3))
print(data)
q.put(data)
def consumer(name,q):
while True:
data=q.get() #
time.sleep(random.randint(1,3))
print('%s 吃了%s '%(name,data))
if __name__ == '__main__':
q=Queue()
p1=Process(target=producer,args=('egon','包子',q))
p2=Process(target=producer,args=('tank','生蚝',q))
c1=Process(target=consumer,args=('lll',q))
c2=Process(target=consumer,args=('yyy',q))
p1.start()
p2.start()
c1.start()
c2.start()
'''
# from multiprocessing import Queue,Process,JoinableQueue
# import time,random
#
# def pruducer(name,food,q):
# for i in range(3):
# data='%s生产了%s%s'%(name,food,i)
# time.sleep(random.randint(1,3))
# print(data)
# q.put(data)
#
# # pass
# def consumer(name,q):
# while True:
# data=q.get()
# if data is None:break
# time.sleep(random.randint(1,3))
# print('%s 吃了%s'%(name,data))
# q.task_done()#告诉你的队列,你已经将数据取并且处理完毕
#
# if __name__ == '__main__':
# q=JoinableQueue()
#
# # # p_list=[]
# # for i in range(2):
# # p=Process(target=pruducer,args=('name',i,q))
# # p.start()
# # # p_list.append(i)
# # for i in range(2):
# # c=Process(target=consumer,args=('lll',i,q))
# # c.start()
# p1=Process(target=pruducer,args=('egon','包子',q))
# p2=Process(target=pruducer,args=('tank','牛肉',q))
# c1=Process(target=consumer,args=('yyy',q))
# c2=Process(target=consumer,args=('mmm',q))
# p1.start()
# p2.start()
# # c1.daemon=True
# # c2.daemon=True
# c1.start()
# c2.start()
# #等待生产者生产完所有的数据
# p1.join()
# p2.join()
# #在生产者生产完数据之后往队列里面放一个提示性的消息,告诉消费者已经没有数据了,你走吧,不要等了
# # q.put(None)
# # q.put(None)
# q.join()#等待队列中数据全部取出
# print('zhu')
import time,random
from multiprocessing import Queue,Process,JoinableQueue
def producer(name,food,q):
for i in range(10):
res='%s%s'%(food,i)
time.sleep(random.randint(1,3)) #模拟生产者产生数据的时间
q.put(res)
print('厨师%s 生产了%s'%(name,res))
# q.put(None)#放在这里会出现有的消费者还没有吃完就让滚蛋的情况,应该放在所有数据的最后
def constumer(name,q):
while True:
# data=q.get() #没数据就一直在等待,处于阻塞,关键在于什么时候死
data = q.get()
# if data==None:break
time.sleep(random.randint(1,3)) #模拟消费者处理数据的时间
print('吃货%s吃了%s'%(name,data))
q.task_done()
if __name__ == '__main__':
q=JoinableQueue
# q=Queue()
#生产者们
p1=Process(target=producer,args=('egon','包子',q))
p2=Process(target=producer,args=('liuxx','肉',q))
#消费者们
c1=Process(target=constumer,args=('json',q))
c2=Process(target=constumer,args=('tank',q))
c1.daemon=True
c2.daemon=True #守护进程
#启动进程,一共有5个
p1.start()
p2.start()
c1.start()
c2.start()
# p1.join()
# p2.join()
# q.put(None)#有几个消费者就放几个none
# q.put(None)
p1.join()
p2.join()#生产者全部生产完了
q.join()#主进程等q结束,即q内的数据被取干净了
print('主')
线程理论
'''
工厂生产橡胶
车间和流水线的关系
1.什么是线程
进程:资源单位(占用的是内存空间独立的)
线程:执行单位(被CPU执行的)
注意:每一个进程中都会自带一个线程
2.为什么要有线程
开一个进程:
申请内存空间 耗时
将代码拷贝到申请的内存空间中 耗时
开线程:
不需要申请内存空间
不需要将代码拷贝到申请的内存空间
开线程的开销远远小于开进程的开销!!!
3.如何使用线程
'''
工厂生产橡胶
一个车间和里面的机器
车间:存放机器的,如果需要多个任务同时开始,就放多个机器
机器:生产具体的产品,需要资源就找车间要
什么是线程?
进程:资源单位(占用的是内存空间,彼此之间是独立的)
线程:执行单位(直接被CPU执行的)
注意:每一个进程都自带一个线程
创建线程的两种方式
1.自己定义一个函数,调用Thread传参(target=’自己写的函数名‘,args=(v1,))
2.写一个类,继承Thread,在里面固定写一个run方法
注意:线程开启速度非常快,几乎代码执行完毕线程就会开启
所以会出现先打印子线程在打印主线程
'''
from threading import Thread
import time
def task(name):
print('%s is running'%name)
time.sleep(2)
print('%s is ending'%name)
if __name__ == '__main__':
t=Thread(target=task,args=('egon',))
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 ending'%self.name)
if __name__ == '__main__':
t=Mythread('json')
t.start() #开启线程的速度超级快,几乎代码执行完毕线程就会开启
print('主')
join方法
让主线程等待子线程程序运行结束,不会影响子线程的执行过程!
会将并发变成串行
线程对象的其他属性和方法
active_count() #查看当前正在活跃的线程数
current_thread().name#查看线程和子线程
os.getpid()#查看线程id
守护线程
from threading import Thread
import time,random
def task(name):
print('%s is running'%name)
time.sleep(2)
print('%s is over'%name)
if __name__ == '__main__':
t=Thread(target=task,args=('aaa',))
t.daemon=True
t.start()
print('主')
主线程要等待所有非守护线程的结束才可以结束
线程间数据是否隔离?
数据共享
from threading import Thread
x=10
def task():
global x
x=666
t=Thread(target=task)
t.start()
t.join()
print(x)
线程互斥锁
from threading import Thread,Lock
import time
mutex=Lock()
n=100
def task():
global n
mutex.acquire()
temp=n
time.sleep(0.1) #模拟网络延迟
n=temp-1
mutex.release()
l_list=[]
for i in range(100):
t=Thread(target=task)
t.start()
l_list.append(t)
for t in l_list:
t.join()
print(n) #0
将并发变成串行