6,线程-线程队列-线程池-协程-greenlet-gevent

线程队列-线程池-协程-greenlet-geventcss

线程队列

队列的各类方法:前端

put:往队列里面放数据python

get:从队列取数据web

put_nowait:面试

get_nowait,不阻塞,队列还有值的时候,直接跳过,没有值的时候报错(但要是get的话,会阻塞),正则表达式

full,检查队列是否满了,返回bool值安全

empty:检查队列是否空了,返回bool值多线程

qsize:输出队列的大小,并发

自带锁,数据安全,app

queue队列:使用import queue,用法与进程Queue同样,先进先出

queue.LifoQueue:后进先出,(栈)

queue.Priority():存储数据时可设置优先级的队列,

from queue import PriorityQueue

q=PriorityQueue()  #优先级队列
q.put(12,'bbb')
q.put(2,'abb')
q.put(20,'vbb')
q.put(1,'bbb')
print(q.get())
print(q.get())
print(q.get())
print(q.get())
输出: 按数字大小输出,其实是按ascii码的前后输出的,
1
2
12
20

 

Python标准模块--concurrent.futures

Threading 没有线程池,multiprocessing,有进程池,concurrent.futures:帮助开发人员管理线程池和进程池,

from threading import currentThread,get_ident

from concurrent.futures import ThreadPoolExecutor(启动线程池的类)

from concurrent.futures import ProcssPoolExecutor(启动进程池的类)

 

基本介绍

 1 #1 介绍
 2 concurrent.futures模块提供了高度封装的异步调用接口
 3 ThreadPoolExecutor:线程池,提供异步调用
 4 ProcessPoolExecutor: 进程池,提供异步调用
 5 Both implement the same interface, which is defined by the abstract Executor class.
 6 
 7 #2 基本方法
 8 #submit(fn, *args, **kwargs)
 9 异步提交任务
10 
11 #map(func, *iterables, timeout=None, chunksize=1) 
12 取代for循环submit的操做
13 
14 #shutdown(wait=True) 
15 至关于进程池的pool.close()+pool.join()操做
16 wait=True,等待池内全部任务执行完毕回收完资源后才继续
17 wait=False,当即返回,并不会等待池内的任务执行完毕
18 但无论wait参数为什么值,整个程序都会等到全部任务执行完毕
19 submit和map必须在shutdown以前
20 
21 #result(timeout=None)
22 取得结果
23 
24 #add_done_callback(fn)
25 回调函数

 

submit,map,shutdown的结合用法

 1 import time
 2 from threading import currentThread,get_ident #查看当前的线程(currentThread)
 3 from concurrent.futures import ThreadPoolExecutor
 4 
 5 def func(i):
 6     print('in %s' %currentThread(),get_ident(),i)#显示当前的线程(包含线程ID,跟线程名)
 7     #输出的线程ID有重复的,证实用到了回收的线程
 8 
 9 t=ThreadPoolExecutor(5)#实例化一个线程,开启线程池,这里5个线程
10 
11 for i in range(20):
12     t.submit(func,i)#异步提交任务,这里进源码看submit,函数传参的问题
13 t.shutdown()#至关于进程池的Pool.close()+Pool.join(),默认为True,为真时,才起做用,
14 print('main:',currentThread(),get_ident()) #主线程
15 
16 
17 两个实现起线程池的方法的区别,对比输出结果,
18 
19 
20 from threading import current_thread,get_ident
21 from concurrent.futures import ThreadPoolExecutor
22 
23 def func(i):  #必需要进行传参,
24     print(888,current_thread())
25 
26 t=ThreadPoolExecutor(5)#线程池,起5个线程,
27 t.map(func,range(20))#map至关于,把可迭代对象的每一个值取出来传给func函数,

 

result(timeout)

 1 import os,time
 2 from concurrent.futures import ThreadPoolExecutor
 3 
 4 def func(i):
 5     time.sleep(0.5)
 6     print(i,)
 7     return i+20
 8 
 9 t=ThreadPoolExecutor(5)
10 lst=[]
11 for i in range(20):
12     ret=t.submit(func,i)
13     # print(ret.result())  #取得结果,
14     lst.append(ret)
15 t.shutdown()
16 for ret in lst:   #对比上面的结果,上面的又变成同步的了,因此须要建立一个了列表
17     print(ret.result())
18 print('主线程')

add_done_callback(fn):回调函数

线程池的回调函数执行,是在线程池里的各个线程.

from threading import get_ident
from concurrent.futures import ThreadPoolExecutor

def func(i):
    print(i,get_ident())
    return get_ident()

def back(fn):
    print('in back',fn.result())#返回的是函数的地址,取值的话,须要.result()

t=ThreadPoolExecutor(5)
for i in range(20):
    t.submit(func,i).add_done_callback(back)#回调函数,

 

进程版本的各个例子

import os
from concurrent.futures import ProcessPoolExecutor

def func(i):
    print(i,os.getpid())
    return i*3

def back(fn):
    print(fn.result(),os.getpid())

if __name__ == '__main__':
    p=ProcessPoolExecutor(5)
    lst=[]
    for i in range(20):
        ret=p.submit(func,i)#得到返回值
        lst.append(ret)
        # p.submit(func,i).add_done_callback(back)#回调函数
    p.shutdown()
    for ret in lst:
        ret.result()

 

 

 

 

进程/线程池总结:

multiprocessing模块自带进程池的
threading模块是没有线程池的
concurrent.futures 进程池 和 线程池
高度封装
进程池/线程池的统一的使用方式
建立线程池/进程池 ProcessPoolExecutor ThreadPoolExecutor
ret = t.submit(func,arg1,arg2....) 异步提交任务
ret.result() 获取结果,若是要想实现异步效果,应该是使用列表
map(func,iterable)
shutdown close+join 同步控制的
add_done_callback 回调函数,在回调函数内接收的参数是一个对象,须要经过result来获取返回值
回调函数仍然在主进程中执行 进程池
回调函数是在池子里的各个线程执行 线程池

概念性的面试题
web框架
爬虫/自动化开发
爬虫 : 访问大量的网页,对网页代码进行处理
正则表达式
字符串处理
前端
并发

运维 : 一堆机器 一堆程序 你去维护
自动化开发/运维开发 : 开发一些程序 让机器的维护/程序的维护自动化起来
运维基础
python的基础开发
并发
前端

 

 

 

协程

 

 

以前咱们学习了线程、进程的概念,了解了在操做系统中进程是资源分配的最小单位,线程是CPU调度的最小单位。按道理来讲咱们已经算是把cpu的利用率提升不少了。可是咱们知道不管是建立多进程仍是建立多线程来解决问题,都要消耗必定的时间来建立进程、建立线程、以及管理他们之间的切换。

  随着咱们对于效率的追求不断提升,基于单线程来实现并发又成为一个新的课题,即只用一个主线程(很明显可利用的cpu只有一个)状况下实现并发。这样就能够节省建立线进程所消耗的时间。

  为此咱们须要先回顾下并发的本质:切换+保存状态

   cpu正在运行一个任务,会在两种状况下切走去执行其余的任务(切换由操做系统强制控制),一种状况是该任务发生了阻塞,另一种状况是该任务计算的时间过长

    

  ps:在介绍进程理论时,说起进程的三种执行状态,而线程才是执行单位,因此也能够将上图理解为线程的三种状态 

  

   对于单线程下,咱们不可避免程序中出现io操做,但若是咱们能在本身的程序中(即用户程序级别,而非操做系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另一个任务去计算,这样就保证了该线程可以最大限度地处于就绪态,即随时均可以被cpu执行的状态,至关于咱们在用户程序级别将本身的io操做最大限度地隐藏起来,从而能够迷惑操做系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给咱们的线程。

    协程的本质就是在单线程下,由用户本身控制一个任务遇到io阻塞了就切换另一个任务去执行,以此来提高效率。为了实现它,咱们须要找寻一种能够同时知足如下条件的解决方案:

 
#1. 能够控制多个任务之间的切换,切换以前将任务的状态保存下来,以便从新运行时,能够基于暂停的位置继续执行。
#2. 做为1的补充:能够检测io操做,在遇到io操做的状况下才发生切换



协程介绍

协程:是单线程下的并发,又称微线程,纤程.英文名:Coroutine,
协程是一种用户态的轻量级线程,即协程是由用户程序本身控制调度的.
注意:
python的线程属于内核级别的,即由操做系统控制调度,(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其余线程执行)
单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操做系统)控制切换,依次来提高效率(非io操做的切换去效率无关)


对比操做系统控制线程的切换,用户在多线程内控制协程的切换,

优势:
1,协程的切换开销更小,属于程序级别的切换,操做系统彻底感知不到,于是更加轻量级
2,多线程内就能够实现并发的效果,最大限度地利用cpu.

缺点:
1,协程的本质是单线程下,没法利用多核,能够是一个程序开启多个进程,每一个进程内开启多个线程,每一个线程内开启协程,
2,协程指的是单个线程,于是一旦协程出现阻塞,将会阻塞整个线程,

总结协程特色:
1,必须在只有一个单线程里实现并发
2,修改共享数据不需加锁
3,用户程序里本身保存多个控制流的上下文栈
4,附加:一个协程遇到IO操做自动切换到其它协程(如何实现检测IO,yield,greenlet都没法实现,就用到了gevent模块)


Greenlet模块

安装:在cmd里输入命令:pep3 install greenlet

yield在两个函数之间的应用.

解说yield的执行
 1 def func():
 2     print(22)
 3     x=yield 'aaa'
 4     print(x)
 5     print(555)
 6     y=yield 'bbb'
 7     print(y)
 8     print(666)
 9 
10 g=func()            #拿到一个生成器函数,执行到此行,没有打印任何东西
11 print(next(g))      #执行到此,打印22,拿到yield的返回值:aaa,共打印:222,aaa,而后阻塞,没有再输出
12 print(g.send(333)) #执行到此,遇到send,继续执行把333,传给第三行的yield,而后打印333,打印555,拿到yield的返回值:bbb,共输出:333,555,bbb
13 
14 # 输出:
15 # 22
16 # aaa
17 # 333
18 # 555
19 # bbb
 
 1 def func1():
 2     while 1:
 3         print(222)
 4         x=yield
 5         print(x)
 6         print(666)
 7 
 8 def func2():
 9     g=func1()
10     next(g)
11     for i in range(3):
12         g.send(i)
13 func2()
14 
15 # 输出:
16 # 222
17 # 0
18 # 666
19 # 222
20 # 1
21 # 666
22 # 222
23 # 2
24 # 666
25 # 222
yield在两个函数之间的使用
 

用yield实现消费者和生产者模型,

 1 def consumer():
 2     while True:
 3         x = yield
 4         print(x)
 5 def producer():
 6     g = consumer()
 7     next(g)   # 预激
 8     for i in range(10):
 9         g.send(i)
10 producer()

 

 

yield只有程序之间的切换,没有重利用任何IO操做的时间,

程序执行的上下文切换的工具.

 

greenlet,gevent,第三方模块,安装,

1,cmd命令行:输出命令:pip3 insatll greenlet(若是是python2版本的,就是pip2)

2,在pycharm里面,文件-->设置-->项目-->Project Iterpreter-->右上角(加号)-->在输入框输入要安装的模块-->左下角-->Install Package-->回上一级菜单-->肯定-->重启pycharm(或者笔记本)

greenlet程序上下文切换

 

使用greenlet模块

 

 1 import time
 2 from greenlet import greenlet
 3 def eat():
 4     print('')
 5     time.sleep(1)
 6     g2.switch()#执行到此,切换到play函数,且下次今后处开始接着执行
 7     print('吃完了')
 8     time.sleep(1)
 9     g2.switch()#执行到此,切换到play函数,
10 def play():
11     print('玩儿')
12     time.sleep(1)
13     g1.switch() #执行到此,切换到eat函数,
14     print('玩美了')
15     time.sleep(1)
16 g1=greenlet(eat)#greenlet是一个类,此行是实例化了一个对象,传一个eat函数
17 g2=greenlet(play)
18 g1.switch() #切换,开启g1,此处再也不是start(),要注意,
19 
20 # 输出:
21 #
22 # 玩儿
23 # 吃完了
24 # 玩美了
 
gevent模块

安装:pip3 install gevent(cmd命令行处输入命令安装)
greenlet是gevent的低层
gevent是基于greenlet实现的
python代码在控制程序的切换

使用gevent来写greenlet的例子
import gevent
import time

def eat():
    print('')
    time.sleep(2)
    print('吃完了')

def play():
    print('玩儿')
    time.sleep(1)
    print('玩儿美了')

g1 = gevent.spawn(eat)   #此处没有详细解释spawn
g2 = gevent.spawn(play)  #单纯的代码写到这里,执行的话,什么都没有打印(输出),
# gevent.sleep(0.5)#可使用此行代码启动,遇到IO才切换,
# g1.join()#使用join是等待eat函数执行完,
# g2.join()
gevent.joinall([g1,g2])#同上面的g1或g2.join()是同样的,

# 输出:
#
# 玩儿
# 玩儿美了
# 吃完了
gevent帮你作了切换,作切换是有条件的,遇到IO才切换
gevent不认识除了gevent这个模块内之外的IO操做
使用join能够一直阻塞直到协程任务完成
帮助gevent来认识其余模块中的阻塞
from gevent import monkey;monkey.patch_all()写在其余模块导入以前
相关文章
相关标签/搜索