python 网络编程 并发编程3(协程,上下文管理器)

一.协程Coroutine:主要面向IO操作
1.概念:又称微线程/纤程,非抢占式,用户态的切换自己规定

协程切换没有消耗硬件资源;OS级别上无法识别协程,只将其视为一个进程中的一个线程,协程只是一个名字,利用yield等进行切换;本质上就是一个线程,没有物理层面上的切换

2.优缺点:

  • 执行效率高:子程序切换不是线程切换,而由程序自身控制;因此没有线程切换的开销;和多线程比,线程数越多,优势越大
  • 不需要多线程的锁机制:只有一个线程,不存在同时写变量的冲突,在协程中控制共享资源不用加锁,只需判断状态即可
  • 只有协程无法利用多核,可以利用多进程+协程解决

3.yield的简单实现:底层实现机制

import queue,time

def consumer(name):
    print("--->ready to eat baozi...")
    while True:
        new_baozi = yield   #利用yield保存状态
        print("[%s] is eating baozi %s" % (name,new_baozi))
        #time.sleep(1)

def producer():
    r = con.__next__()
    r = con2.__next__()
    n = 0
    while 1:
        time.sleep(1)
        print("producer is making baozi %s and %s" %(n,n+1) )
        con.send(n)   #send()之前必须经过__next__()
        con2.send(n+1)
        n +=2

if __name__ == '__main__':
    con=consumer("c1")   #创建生成器,不执行
    con2=consumer("c2")
    p=producer()
    
#结果:
#--->ready to eat baozi...
#--->ready to eat baozi...
#producer is making baozi 0 and 1
#[c1] is eating baozi 0
#[c2] is eating baozi 1
#producer is making baozi 2 and 3
#[c1] is eating baozi 2
#[c2] is eating baozi 3

4.Greenlet模块:一个用C实现的协程模块(第三方模块)
相比与python自带的yield,可以在任意函数间随意切换而不需把这个函数先声明为generator

from greenlet import greenlet
 
def test1():
    print(12)
    gr2.switch()   #执行test2();test1()挂起
    print(34)
    gr2.switch()
 
def test2():
    print(56)
    gr1.switch()
    print(78)
 
gr1 = greenlet(test1)   #将函数封装进greenlet对象
gr2 = greenlet(test2)
gr1.switch()   #执行gr1(封装进gr1的test1()函数)

5.Gevent模块(第三方模块)

#一个爬虫:
import requests,time,gevent

start=time.time()

def f(url):
    print('GET: %s' % url)
    resp = requests.get(url)   #爬取网页内容;resp即为网页内容
    data = resp.text
    print('%d bytes received from %s.' % (len(data), url))

#利用协程:
gevent.joinall([gevent.spawn(f,'https://www.python.org/'),gevent.spawn(f,'https://www.yahoo.com/'),gevent.spawn(f,'https://www.baidu.com/'),gevent.spawn(f,'https://www.sina.com.cn/'),])

#单进程:
# f('https://www.python.org/')
# f('https://www.yahoo.com/')
# f('https://baidu.com/')
# f('https://www.sina.com.cn/')

print("cost time:",time.time()-start)

二.上下文管理器(contextlib模块)
1.作用:代码块执行前准备,代码块执行后收拾

2.如何使用上下文管理器:

#打开一个文件,并写入"hello world":
filename="my.txt"
mode="w"
f=open(filename,mode)
f.write("hello world")
f.close()

当发生异常时(如磁盘写满),就没有机会执行第5行

#采用try-finally语句块进行包装:
writer=open(filename,mode)
try:
    writer.write("hello world")
finally:
    writer.close()

当我们进行复杂的操作时,try-finally语句就会变得丑陋

#采用with语句重写:
with open(filename,mode) as writer:
    writer.write("hello world")

as指代了从open()函数返回的内容,并把它赋给了新值
with完成了try-finally的任务

3.自定义上下文管理器
with语句的作用类似于try-finally,提供一种上下文机制
要应用with语句的类,其内部必须提供两个内置函数__enter__和__exit__
前者在主体代码执行前执行,后者在主体代码执行后执行
as后面的变量是在__enter__函数中返回的

class echo():
    def output(self):
        print("hello world")
    def __enter__(self):
        print("enter")
        return self  #可以返回任何希望返回的东西
    def __exit__(self,exception_type,value,trackback):
        print("exit")
        if exception_type==ValueError:
            return True
        else:
            return False
  
with echo() as e:
    e.output()
     
#结果:
#enter   #执行__enter__()
#hello world   #执行主体代码
#exit   #执行__exit__()

完备的__exit__函数如下:

def __exit__(self,exc_type,exc_value,exc_tb)
#exc_type:异常类型
#exc_value:异常值
#exc_tb:异常追踪信息

当__exit__返回True时,异常不传播

4.contextlib模块:提供更易用的上下文管理器,无需创建类及__enter__和__exit__这两个方法

通过Generator实现
contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制,常用框架如下:

from contextlib import contextmanager

@contextmanager
def make_context():
    print('enter')
    try:
        yield "ok"
    except RuntimeError:
        print('error',err)
    finally:
        print('exit')
         
with make_context() as value:
    print(value)
     
#结果:
#enter
#ok   #执行主体代码
#exit

yield写入try-finally中是为了保证异常安全(能处理异常)
as后的变量的值由yield返回
yield前的语句可看作代码块执行前操作
yield后的操作可以看作在__exit__函数中的操作

以线程锁为例:
@contextlib.contextmanager
def loudLock():
    lock=Lock()
    print('Locking')
    lock.acquire()
    yield
    print('Releasing')
    lock.release()
 
with loudLock():
    print('Doing something that needs locking')
 
#结果:
#Locking
#Doing something that needs locking
#Releasing

5.contextlib.nested:减少嵌套

with open(filename,mode) as reader:
    with open(filename1,mode1) as writer:
        writer.write(reader.read())

可以通过contextlib.nested进行简化:

with contextlib.nested(open(filename,mode),open(filename1,mode1)) as (reader,writer):
    writer.write(reader.read())

在python 2.7及以后,被一种新的语法取代:

with open(filename,mode) as reader,open(filename1,mode1) as writer:
    writer.write(reader.read())

6.contextlib.closing()
file类直接支持上下文管理器API,但有些表示打开句柄的对象并不支持,如urllib.urlopen()返回的对象;还有些遗留类,使用close()方法而不支持上下文管理器API
为了确保关闭句柄,需要使用closing()为这些对象创建一个上下文管理器(调用类的close方法)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值