线程、进程、协程、同步、异步、阻塞、非阻塞

一、计算机存储

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)   #执行协程对象,参数必须是列表

原文地址:https://www.cnblogs.com/zz27zz/p/8690985.html