目录
一、计算机操作系统介绍
二、进程的概念与应用
三、线程的概念与应用
四、进程池与线程池
五、协程的概念与应用
六、IO模型
一、计算机操作系统介绍
(一)计算机操作系统概念
什么是操作系统(what):
操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序。
为什么有操作系统(why):
程序员无法把所有的硬件操作细节都了解到,管理这些硬件并且加以优化使用是非常繁琐的工作,这个繁琐的工作就是操作系统来干的,有了他,程序员就从这些繁琐的工作中解脱了出来,只需要考虑自己的应用软件的编写就可以了,应用软件直接使用操作系统提供的功能来间接使用硬件。
操作系统的功能:
①隐藏了丑陋的硬件调用接口,为应用程序员提供调用硬件资源的更好,更简单,更清晰的模型(系统调用接口)。应用程序员有了这些接口后,就不用再考虑操作硬件的细节,专心开发自己的应用程序即可。
②将应用程序对硬件资源的竞态请求变得有序化。
(二)计算机操作系统发展史
第一代计算机(1940~1955):真空管和穿孔卡片
工作过程:
特点:
1、没有操作系统的概念
2、所有的程序设计都是直接操控硬件
优点:
程序员在申请的时间段内独享整个资源,可以即时地调试自己的程序(有bug可以立刻处理)
缺点:
浪费计算机资源,一个时间段内只有一个人用。
Tips:
同一时刻只有一个程序在内存中,被cpu调用执行,比方说10个程序的执行,是串行的。
第二代计算机(1955~1965):晶体管和批处理系统
工作过程:
特点:
1、把一堆人的输入攒成一大波输入
2、然后顺序计算(这是有问题的,但是第二代计算也没有解决)
3、把一堆人的输出攒成一大波输出
优点:
批处理,节省了机时。
缺点:
1、整个流程需要人参与控制,将磁带搬来搬去(中间俩小人)
2、计算的过程仍然是顺序计算-》串行
3、程序员原来独享一段时间的计算机,现在必须被统一规划到一批作业中,等待结果和重新调试的过程都需要等同批次的其他程序都运作完才可以(这极大的影响了程序的开发效率,无法及时调试程序)
第三代计算机(1965~1980):集成电路芯片和多道程序设计
解决的问题:
①spooling技术:淘汰了IBM1401机,不必将磁带搬来搬去。
②多道技术:多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或者说共享同一个资源(比如cpu)的有序调度问题,解决方式即多路复用,多路复用分为时间上的复用和空间上的复用。
a)空间上的复用:多个进程共用一个内存条。
b)时间上的复用:多个进程复用同一个CPU时间。
CPU遇到IO切换:可以提成效率。
一个进程CPU占用时间过长也会切走,为了实现并发效果不得已而为之,反而会降低程序执行的效率。
③分时操作系统:多个联机终端+多道技术。
二、进程的概念与应用
什么是进程(what):
进程指的就是一个正在运行的程序,或者说是程序的运行过程,即进程是一个抽象的概念。
进程是起源于操作系统的,是操作系统最核心的概念,操作系统所有其他的概念都是围绕进程展开的。
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
为什么要使用进程(why):
实现并发。
1、串行: 一个任务完完整整地运行完毕后,才能运行下一个任务 2、并发 看起来多个任务是同时运行的即可,单核也可以实现并发 3、并行: 真正意义上多个任务的同时运行,只有多核才实现并行 4、cpu的功能: cpu是用来做计算,cpu是无法执行IO操作的,一旦遇到io操作,应该让cpu去执行别的任务 5、多道技术 1、空间上的复用=》多个进程共用一个内存条 2、时间上的复用-》多个进程复用同一个cpu的时间 cpu遇到IO切换:可以提升效率 一个进程占用cpu时间过长也会切走:为了实现并发效果不得已而为之,反而会降低程序的执行效率
进程状态
tail -f access.log |grep '404'
执行程序tail,开启一个子进程,执行程序grep,开启另外一个子进程,两个进程之间基于管道'|'通讯,将tail的结果作为grep的输入。
进程grep在等待输入(即I/O)时的状态称为阻塞,此时grep命令都无法运行
其实在两种情况下会导致一个进程在逻辑上不能运行,
1. 进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作
2. 与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。
因而一个进程由三种状态
进程并发的实现
进程并发的实现在于,硬件中断一个正在运行的进程,把此时进程运行的所有状态保存下来,为此,操作系统维护一张表格,即进程表(process table),每个进程占用一个进程表项(这些表项也称为进程控制块)
如何使用进程(how):
调用multiprocessing模块,创建进程类
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动) 强调: 1. 需要使用关键字的方式来指定参数 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
group参数未使用,值始终为None target表示调用对象,即子进程要执行的任务 args表示调用对象的位置参数元组,args=(1,2,'egon',) kwargs表示调用对象的字典,kwargs={'name':'egon','age':18} name为子进程的名称
方式一:
from multiprocessing import Process import time def task(x): print('%s is running' %x) time.sleep(3) print('%s is done' %x) if __name__ == '__main__': # Process(target=task,kwargs={'x':'子进程'}) p=Process(target=task,args=('子进程',)) # 如果args=(),括号内只有一个参数,一定记住加逗号 p.start() # 只是在操作系统发送一个开启子进程的信号 print('主')
方式二:
from multiprocessing import Process import time class Myprocess(Process): def __init__(self,x): super().__init__() self.name=x def run(self): print('%s is running' %self.name) time.sleep(3) print('%s is done' %self.name) if __name__ == '__main__': p=Myprocess('子进程1') p.start() #p.run() print('主')
from multiprocessing import Process import time x=100 def task(): global x x=0 print('done') if __name__ == '__main__': p=Process(target=task) p.start() time.sleep(500) # 让父进程在原地等待,等了500s后,才执行下一行代码 print(x)
(一)进程对象相关的属性或方法:
①join():让父进程在原地等待,等到子进程运行完毕后,回收对应子进程的系统资源,再执行下一行代码。
from multiprocessing import Process import time def task(name,n): print('%s is running ' %name) time.sleep(n) print('%s is done ' % name) if __name__ == '__main__': p1=Process(target=task,args=('子进程1',1)) p2=Process(target=task,args=('子进程2',2)) p3=Process(target=task,args=('子进程3',3)) start_time=time.time() p1.start() p2.start() p3.start() p3.join() p1.join() p2.join() stop_time=time.time() print('主',(stop_time-start_time))
from multiprocessing import Process import time def task(name,n): print('%s is running ' %name) time.sleep(n) print('%s is done ' % name) if __name__ == '__main__': p1=Process(target=task,args=('子进程1',1)) p2=Process(target=task,args=('子进程2',2)) p3=Process(target=task,args=('子进程3',3)) start=time.time() p1.start() p1.join() p2.start() p2.join() p3.start() p3.join() stop=time.time() print('主',(stop-start))
②用循环实现创建多个进程
from multiprocessing import Process import time def task(name,n): print('%s is running ' %name) time.sleep(n) print('%s is done ' % name) if __name__ == '__main__': p_l=[] start=time.time() for i in range(1,4): p=Process(target=task,args=('子进程%s' %i,i)) p_l.append(p) p.start() # print(p_l) for p in p_l: p.join() stop=time.time() print('主',(stop-start))
③pid(getpid、getppid)
注意:在join()之后继续执行查看子进程pid的操作(p1.pid)仍然可以打印它的值,其原因是:join()回收的是该子进程占用的操作系统资源(包括pid),而python内的p1.pid仅仅是一个值而已。
from multiprocessing import Process import time import os def task(): print('自己的id:%s 父进程的id:%s ' %(os.getpid(),os.getppid())) time.sleep(1) if __name__ == '__main__': p1=Process(target=task) p1.start() p1.join() print('主',os.getpid(),os.getppid())
④name
from multiprocessing import Process,current_process import time def task(): print('子进程[%s]运行。。。。' %current_process().name) time.sleep(200) if __name__ == '__main__': p1=Process(target=task,name='子进程1') p1.start() # print(p1.name) print('主')
⑤is_alive、terminnate
from multiprocessing import Process,current_process import time def task(): print('子进程[%s]运行。。。。' %current_process().name) time.sleep(2) if __name__ == '__main__': p1=Process(target=task,name='子进程1') p1.start() print(p1.is_alive()) p1.terminate() print(p1.is_alive()) p1.join() print(p1.is_alive())
(二)守护进程
什么是守护进程(what):
守护进程其实就是一个“子进程”
守护=》伴随
守护进程会伴随主进程的代码运行完毕后而死掉。
为什么要用守护进程(why):
进程:
当父进程需要将一个任务并发出去执行,需要将该任务放到一个子进程里
守护:
当该子进程内的代码在父进程代码运行完毕后就没有存在的意义了,就应该
将该子进程设置为守护进程,会在父进程代码结束后死掉。
如何用守护进程(how):
from multiprocessing import Process import time,os def task(name): print('%s is running' %name) time.sleep(3) if __name__ == '__main__': p1=Process(target=task,args=('守护进程',)) p2=Process(target=task,args=('正常的子进程',)) p1.daemon = True # 一定要放到p.start()之前 p1.start() p2.start() print('主') # 主进程代码运行完毕,守护进程就会结束
(三)进程同步(互斥锁)
什么是互斥锁(what):
可以将要执行任务的部分代码(只涉及到修改共享数据的代码)变成串行。
互斥锁与join()的区别:
join是要执行任务的所有代码整体串行。
如何用互斥锁(how):
from multiprocessing import Process,Lock import json import os import time import random def check(): time.sleep(1) # 模拟网路延迟 with open('db.txt','rt',encoding='utf-8') as f: dic=json.load(f) print('%s 查看到剩余票数 [%s]' %(os.getpid(),dic['count'])) def get(): with open('db.txt','rt',encoding='utf-8') as f: dic=json.load(f) time.sleep(2) if dic['count'] > 0: # 有票 dic['count']-=1 time.sleep(random.randint(1,3)) with open('db.txt','wt',encoding='utf-8') as f: json.dump(dic,f) print('%s 购票成功' %os.getpid()) else: print('%s 没有余票' %os.getpid()) def task(mutex): # 查票 check() #购票 mutex.acquire() # 互斥锁不能连续的acquire,必须是release以后才能重新acquire get() mutex.release() # with mutex: # get() if __name__ == '__main__': mutex=Lock() for i in range(10): p=Process(target=task,args=(mutex,)) p.start()
(四)进程间通信(IPC)
什么是IPC(what):
进程彼此之间互相隔离,要实现进程间通信(IPC)。
如何实现进程间通信(how):
两种实现方式:
①PIPE管道(不推荐使用)
暂不介绍
②Queue队列(推荐使用)——>PIPE+锁
Tips:
a)队列占用的是内存空间。
b)不应该往队列中存放大数据,应该只存放数据量较小的信息。
c)先进先出概念
from multiprocessing import Queue #q = Queue([maxsize]) #创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。 #maxsize是队列中允许最大项数,省略则无大小限制。 q=Queue(3)
from multiprocessing import Queue q=Queue(3) #先进先出 #put(obj,block,timeout) #1、obj——任意类型 #2、block=True——允许阻塞 # block=False——不允许阻塞 #3、timeout——设置阻塞时间(当block=True时使用) #4、当block=True,并设置timeout时间时,程序会在等待timeout时间#后抛出异常,显示队列已满full #5、当block=False,程序直接抛出异常,显示队列已满full q.put('first') q.put({'k':'sencond'}) q.put(['third',]) #q.put(4) #如果这里输入上行注释内容,程序会一直等待,因为队列已满,无法继续#加入队列。 #get(block,timeout) #1、block=True——允许阻塞 # block=False——不允许阻塞 #2、timeout——设置阻塞时间(当block=True时使用) #3、当block=True,并设置timeout时间时,程序会在等待timeout时间#后抛出异常,显示队列已空empty #4、当block=False,程序直接抛出异常,显示队列已空empty print(q.get()) print(q.get()) print(q.get()) #print(q.get()) #如果这里输入上行注释内容,程序会一直等待,因为队列内容已经取完,程序会一直等待队列出现新内容并取走。
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
(五)生产者消费者模型
什么是生产者消费者模型(what)?
生产者:比喻的是程序中负责产生数据的任务
消费者:比喻的是程序中负责处理数据的任务
生产者——>共享的介质(队列)<——消费者
为什么用生产者消费者模型(why)?
实现了生产者与消费者的解耦和,生产者可以不停地生产,消费者也可以不停地消费,
从而平衡了生产者的生产能力与消费者消费能力,提升了程序整体运行的效率。
当我们的程序中存在明显的两类任务,一类负责产生数据,另外一类负责处理数据
此时就应该考虑使用生产者消费者模型来提升程序的效率。
如何使用生产者消费者模型(how)?
from multiprocessing import JoinableQueue,Process import time import os import random def producer(name,food,q): for i in range(3): res='%s%s' %(food,i) time.sleep(random.randint(1,3)) # 往队列里丢 q.put(res) print('