一、计算机存储
RAM:读写存储器,例如计算机内存、手机内存。掉电不保存,读写速度高
ROM:只读存储器,例如计算机硬盘、手机SD卡。掉电依然保存,读速度低
二、计算机CPU
电脑的CPU,手机的处理器。CPU分为单核和双核或多核,单核处理器在微观上只能在同一时刻处理一项任务,例如在电脑上用酷狗听歌,那我们此时也可以同时打开网页浏览新闻,酷狗听歌和网页几乎可以同时操作,这是为什么呢?因为我们的CPU不可能一直处理一件事,假如电脑的任务有一个清单那么长的话,CPU会从第一个开始执行一会儿再运行一会儿第二个任务,之后再处理第三个第四个,只是它处理速度非常快,我们在感官上会觉得电脑在同一时刻可以处理很多事。这里的很多事也可以说很多个进程。
三、进程、线程、协程
在电脑上打开酷狗音乐应用,酷狗音乐比作一个进程,在酷狗音乐里同时下载10首歌曲,这10首歌曲相当于线程。
进程:动态概念,运行的状态。相对独立,系统给进程单独开辟内存空间,数据安全,较完善的进程间通信。
缺点是需要消耗系统资源非常大,占用CPU和 内存,PCB进程控制块专门用来存储进程信息。
线程:是进程的衍生品,一个进程可以包含多个线程。消耗的资源是进程的1/20,依赖于进程而存在。它可以被抢占(中断)和临时挂起(睡眠),这种做法叫做让步。
线程一般是以并发方式执行的。在单核CPU系统中,真正的并发是不可能的,所以线程间每个线程运行一小会儿,然后让步给其他线程
缺点是线程之间是共享进程的资源的,不如进程间传输的数据安全。
协程:在一个进程中,只有一个线程,这个线程一会儿执行A任务,一会儿执行B任务
进程池:原理:进程消耗的资源较大,假如有1000个任务,需要1000个进程,创建1000个进程,销毁1000个进程,对资源的消耗很庞大。
但是如果把进程放在池子里,假如计算机内核是4核,能同时执行4个任务,那么我创建10个进程放在进程池里,挂起这10个进程,1000个任务随机10个进入进程池,自然会有10个进程去处理这10个事件,10个事件处理完之 后,进程池里的10个进程不销毁,仍然再跑着,等着另外10个再跑进来,如果不到10个任务那就闲置几个进程。避免了进程的频繁创建和销毁。就像10个服务员始终待命一样。有事儿就让他们做。
进程与进程间是独立的,例如登录一个APP需要用微信登录,或微博登录,那这个APP是个单独的进程,微信也是单独的进程,两个进程之间需要通信才能完成登录的操作。
四、计算机IO
IO代表input输入output输出,分为IO设备和IO接口,
IO设备包含存储设备,磁盘、光盘、硬盘等,键盘、监视器、打印机等。
IO接口指单片机与外设的IO接口芯片。
IO端口指的是IO接口电路中带有端口地址的寄存器或缓冲器,IO设备的单片机通过端口地址就可以对端口中的信息进行读写,传数据的端口叫做数据口,传命令的端口叫命令口,传状态的端口叫做状态口。一个IO接口上可以有多个IO端口
IO请求:
阻塞IO:资源不可用时,IO请求一直阻塞,直到反馈结果(有数据或者超时)
非阻塞IO:资源不可用时,IO请求离开,返回数据标识资源不可用
同步IO:应用阻塞在发送或接受数据的状态,直到数据成功传输或返回失败。理解:提交请求-->等待服务器处理-->处理完毕返回 这个期间客户端不能干任何事(比如B/S模式)
异步IO:应用发送或接收数据后立刻返回,数据写入系统缓存,用系统完成数据发送或接收,并返回成功或失败的信息给应用。理解:请求通过事件触发-->服务器处理(这时客户端仍然可以做其他事情)-->处理完毕(比如AJAX异步技术)
多进程、多线程优点:并行执行多任务,为了提高执行效率,处理速度更快一些。
多进程、多线程缺点:占用系统资源
进程:动态概念,运行的状态。相对独立,系统给进程单独开辟内存空间,数据安全,较完善的进程间通信。
缺点:需要消耗系统资源非常大,占用CPU和 内存,PCB进程控制块专门用来存储进程信息。
线程:是进程的衍生品,一个进程可以包含多个线程。消耗的资源是进程的1/20,依赖于进程而存在。
缺点:线程之间是共享进程的资源的,不如进程间传输的数据安全
程序:程序是静态的,程序执行的过程是动态的,从开始到结束的过程称为程序的生命周期,是一个动态的概念。
进程的
os模块操作底层系统相关。应用层调用内核程序来创建多线程再来反馈给应用层
新建进程:
1.fork()方法
#只在POSIX系统上可用,windows版没有
import os
pid=os.fork() #复制了一个进程,现在有两个进程了,主进程(pid默认为大于0的)和子进程(pid等于0)
if pid==0: #子进程执行
xxx elif pid>0: #主进程执行 xxx
#程序本身是个主进程,主进程和子进程谁先开始执行不一定,感官上是同时执行,微观上可能执行顺序不是同一时刻
进程有几种状态:
1.就绪:准备要执行,CPU的时间片还没有轮到他
2.运行态:运行状态
3.等待态:遇到阻塞了,等待某种条件的触发
4.停止态:停止了,但是没有被销毁
5.僵尸态:进程结束,父进程没有对子进程进行收尸处理,占用资源,损害内存
2.multiprocessing类
更方便的管理
import multiprocessing #标准库模块
import os
p=multiprocessing.Process(target=worker,name="myprocess",args=(2,5)) #创建子进程
#target参数为一个函数名,启动进程的时候,执行哪个函数
#name参数为 给进程起个名字,不起名字的时候,默认为process1 ,2,3
#args参数为 第target函数的参数
#p表示进程对象
jobs=[ ] jobs.append(p) #把子进程加入列表中是为了方便父进程对他回收 p.start() #启动子进程 for i in jobs: i.join() #回收子进程,join()是个阻塞函数,只有子进程执行完,才能冲破这个阻塞 print os.getpid() #进程的编号就是getpid,获取当前进程的pid
子进程如果要修改全局变量,在子进程结束后这个全局变量恢复原来的值。
进程池:
原理:进程消耗的资源较大,假如有1000个任务,需要1000个进程,创建1000个进程,销毁1000个进程,对资源的消耗很庞大。
但是如果把进程放在池子里,假如计算机内核是4核,能同时执行4个任务,那么我创建10个进程放在进程池里,挂起这10个进程,1000个任务随机10个进入进程池,自然会有10个进程去处理这10个事件,10个事件处理完之后,进程池里的10个进程不销毁,仍然再跑着,等着另外10个再跑进来,如果不到10个任务那就闲置几个进程。避免了进程的频繁创建和销毁。就像10个服务员始终待命一样。有事儿就让他们做。
import multiprocessing
p=multiprocessing.Pool(processes=4) #进程池里有4个进程,p是进程池对象,而不是进程对象
result=[] for i in range(10) #一共有10个事件 msg="hello" result.append(p.apply_async(worker,(msg,))) #apply_async异步处理 ,先把4个事件添加到进程池里,另外6个处于等待状态,worker为处理函数,(msg,)为worker函数参数 #apply是逐个处理,每次只给进程池添加一个事件,一个一个执行属于阻塞函数 #result为事件对象 for res in result: #取出事件对象 print res.get() #得到事件返回值 p.close() #阻塞函数,把进程池关闭掉,不让新的任务添加了 p.join() #阻塞函数,回收进程池
进程间通信:
进程与进程间是独立的,例如登录一个APP需要用微信登录,或微博登录,那这个APP是个单独的进程,微信也是单独的进程,两个进程之间需要通信才能完成登录的操作。
IPC是一组编程接口,通信方式。包括:明管道、共享内存、消息队列、信号灯、信号、socket
进程间用全局变量是不可行的,因为子进程即使对全局变量做了更改,父进程也收不到这个更改,子进程的内容和父进程的内容完全是分隔的。是两块不同的内存
管道通信:
from multiprocessing import Process,Pipe
process_list=[] parent_conn,child_conn=Pipe() #管道就是在系统内存中开辟的信道,双向管道,父进程和子进程都可见 def f(name): child_conn.send(["111"+str(name)]) #往管道里发送值 print os.getppid() ,os.getpid() #打印父进程的进程号和子进程的进程号 for i in range(10): p=Process(target=f,args=(i,)) #f是个函数名 p.start() process_list.append(p) for j in process_list: j.join() for p in range(10): print parent_conn.recv() #父进程收到那10个进程往管道里发送的值并打印出来
信号:
代表某种含义的信号,而不是传输字符串,信号可以用数字符号表示也可以用大写英文字符表示
在linux中输入kill -l列出的信号列表
linux下常见信号例如:
SIGINT 通常在ctrl+c时发出 默认操作-终止
import os
os.kill(pid,sig) #pid进程编号,sig为数字符号或者大写英文字符
import signal
signal.signal(signal.SIGINT,myHandler) #处理信号,不是阻塞函数,异步
#第1个参数是要处理的信号,只有接收到这个信号的时候才会处理,接收到别的信号不会处理。
#第2个参数是处理的方式,有三种可能,1)SIG_DFL默认方式处理;2)SIG_IGN忽略处理;3)function传函数,这个函数只能有两个参数,第一个参数是接收到的信号,第二个是信号类型
signal.pause() #等待信号,阻塞函数
终端就是一个进程,按ctrl+c就是一个进程给另一个进程发消息
消息队列:
在内存中分配空间,一个消息一个消息的来放,一个消息一个消息的来取,以个体为单位来使用。
from multiprocessing import Process,Queue
q=Queue() #创建消息队列
def f(name): time.sleep(1) #每个一秒放一个字符串 q.put(['hello'+str(name)]) #每个进程可以放字符串,也可以放一个列表 for i in range(10): p=Process(target=f,args=(i,)) p.start() process_list.append(p) for j in process_list: j.join() for i in range(10): print q.get()
pool=Pool(5)
pool.map(函数名字,函数的参数列表) 等价于 result.append(p.apply_async(run,(i,)))
map(函数名字,函数的参数列表) 内建函数
线程:
像线程一样管理进程
thread模块(Python3中已经不用)和threading模块(功能更强大)
threading模块对象
Thread类
Lock类 同步与互斥处理
RLock类 同步与互斥
Condition类 同步与互斥
Event类 同步与互斥
Semaphore类 同步与互斥
BoundedSemaphore类
Timer类
import threading
threads=[] t1=threading.Thread(target=music,args=('baby',)) #创建一个线程 t2=threading.Thread(target=move,args=('afraid',)) #创建第二个线程 threads.append(t1) threads.append(t2) for t in threads: t.setDaemon(False) #默认为false,主线程执行完不退出,为true时,主线程执行完退出 t.start() #启动线程,线程1执行music函数,线程2执行move函数 for t in threads: t.join() #线程的回收 #当我有单个线程的时候,这个叫线程或者进程是没啥区别的 #当我创建两个线程的时候,原来的程序的线程叫做主线程,另外两个是分支线程,现在一共有三个线程 #线程和进程的区别就是,主程序中有个global变量,子进程对global变量进行修改后不影响原来的值,改变只在当前子进程中有效,其他进程读取global变量无影响; #但是分支线程修改全局变量后影响其他线程对global的取值,对其他线程有影响,此时global变量叫做争夺变量
1.Event事件
import threading
from time import sleep,ctime e=threading.Event() #创建一个事件,对所有线程都可见 t1=threading.Thread(name='block',target=wait_for_event,args=(e,) ) #创建线程 ti.start() t2=threading.Thread(name='nonblock',target=wait_for_timeout,args=(e,3)) t2.start() sleep(4) e.set() def wait_for_event(e): print e.wait() #阻塞函数,直到执行e.set() ,才执行这条语句,返回值为布尔类型 def wait_for_event_timeout(e,t): while not e.isSet(): #判断这个事件是否被设置 print e.wait(t) #e.wait(t)阻塞函数,等待e.set()的执行,如果超过t秒就不等了,也就是最多等t秒,然后执行这条语句,返回布尔类型值,如果是因为到达t秒了才执行就会返回false,如果是因为等到e.set()了返回值就会true
2.Lock锁
import threading
from time import sleep a=b=0 lock=threading.Lock() #创建锁对象 t=threading.Thread(target=value) #创建线程 t.start() #线程执行 while True: a+=1 b+=1 def value(): while True: if a!=b: print a,b #因为线程之间对全局变量的数据是相互影响,所以a和b的值可能不会相等 ---------方式二 while True: lock.acquire() #在此处加锁 a+=1 b+=1 lock.release() #直到解锁结束,第二个锁才会冲破阻塞 def value(): while True: lock.acquire() #这块再加锁时,程序变阻塞,跟上面那个lock.acquire()谁先执行不一定,反正执行第二个acquire()时会变阻塞 if a!=b: print a,b #这样的话,a和b一定是相等的了 lock.release() #意思就是value函数和主程序中的while true循环只能有一个地方在执行
JIL:
IO密集型适合使用多进程,使用多核
协程:可以用来处理多线程IO高并发的处理方法
计算密集型:使用多进程
IO密集型:使用多线程
如果计算密集和IO密集都有,使用进程+协程(单线程)
协程的实现方式:在处理很多事件的时候,可能有一个事件处理一半就开始阻塞了,那么它就会跳出来执行其他事件,等这个阻塞被冲破了它再回过头来执行这个事件,这样就大大节省了处理时间
yield关键字只能放在函数当中,这样的函数就变成一个生成器,作用就是在函数执行过程中跳出来
协程就是单线程或单进程
1.Greenlet协程:
安装pip greenlet
from greenlet import greenlet
gr1=greenlet(test1) #注册协程1,test1为函数名称,把函数当成注册对象
gr2=greenlet(test2) #注册协程2 gr1.switch() #启动选择器,选择启动gr1这个协程,开始执行test1函数 def test1(): print "12" #第一步打印12 gr2.switch() #记录函数栈帧,切换到gr2协程上,开始执行test2函数 print "34" #第三步打印34 def test2(): print "56" #第二步打印56 gr1.switch() #切换到gr2上,开始执行test1函数 print "78"
2.gevent类
import gevent
from time import sleep
def foo():
print "x"
gevent.sleep(2) #IO事件的阻塞,sleep(2) 不是IO阻塞,是程序阻塞
print "y"
def bar():
print 'a'
gevent.sleep(1)
print 1
def func3():
print 'b'
gevent.sleep(0)
print 'c'
l=[gevent.spawn(foo),gevent.spawn(bar),gevent.spawn(func3)] #注册3个协程对象
gevent.joinall(l) #执行协程对象,参数必须是列表