zoukankan      html  css  js  c++  java
  • python-day37--协程

    一、 协程介绍

    单线程下实现并发,提升运行效率,

      1.自己控制切换,保存状态

      2.遇到I/O切         (单纯的CPU切没意义,只有在遇到I/O的时候切才有效率)

    一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。、

    需要强调的是:

    #1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
    #2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

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

    优点如下:

    #1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
    #2. 单线程内就可以实现并发的效果,最大限度地利用cpu

    缺点如下:

    #1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
    #2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

    总结协程特点:

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

    二、用yeild实现协程

     1 import time
     2 def init(func):
     3     def wrapper(*args,**kwargs):
     4         g=func(*args,**kwargs)
     5         next(g)
     6         return g
     7     return wrapper
     8 @init
     9 def consumer():
    10     while True:
    11         x=yield
    12         print(x)
    13 
    14 def producer(target):
    15     for i in range(10):
    16         # time.sleep(1)
    17         target.send(i)
    18 
    19 producer(consumer())
    View Code

    三、greenlet模块(了解就行,遇到I/O不能切)

     1 from greenlet import greenlet
     2 import time
     3 def eat(name):
     4     print('%s eat 1' %name)
     5     time.sleep(10)      #在io阻塞的时候 不能来回切,会一直在这睡着
     6     g2.switch('egon')
     7     print('%s eat 2' %name)
     8     g2.switch()
     9 def play(name):
    10     print('%s play 1' %name)
    11     g1.switch()
    12     print('%s play 2' %name)
    13 
    14 g1=greenlet(eat)
    15 g2=greenlet(play)
    16 
    17 #g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要
    View Code

    四、gevent模块

    遇到IO阻塞时会自动切换任务

    用法

    #用法
    g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的
    
    g2=gevent.spawn(func2)
    
    g1.join() #等待g1结束
    
    g2.join() #等待g2结束
    
    #或者上述两步合作一步:gevent.joinall([g1,g2])
    
    g1.value#拿到func1的返回值
     1 from gevent import monkey;monkey.patch_all()    #monkey;monkey.patch_all()  可以识别其他的I/O操作
     2 import gevent
     3 import time,threading
     4 def eat(name):
     5     print('%s eat 1' %name)
     6     time.sleep(2)
     7     print('%s eat 2' %name)
     8     return 'eat'
     9 
    10 def play(name):
    11     print('%s play 1' %name)
    12     time.sleep(3)
    13     print('%s play 2' %name)
    14     return 'play'
    15 
    16 start=time.time()
    17 g1=gevent.spawn(eat,'egon')
    18 g2=gevent.spawn(play,'egon')
    19 # g1.join()
    20 # g2.join()
    21 gevent.joinall([g1,g2])
    22 print('',(time.time()-start))
    23 print(g1.value)
    24 print(g2.value)
    25 
    26 # 结果:
    27 egon eat 1
    28 egon play 1
    29 egon eat 2
    30 egon play 2
    31 主 3.0018091201782227
    32 eat
    33 play

    五、协程的应用

    练习题1

     1 from gevent import monkey;monkey.patch_all()
     2 import gevent
     3 import requests
     4 import time
     5 
     6 def get_page(url):
     7     print('GET: %s' %url)
     8     response=requests.get(url)
     9     if response.status_code == 200:
    10         print('%d bytes received from %s' %(len(response.text),url))
    11 
    12 start_time=time.time()
    13 
    14 # get_page('https://www.python.org/')   #串行
    15 # get_page('https://www.yahoo.com/')    #串行
    16 # get_page('https://github.com/')   #串行
    17     
    18 g1=gevent.spawn(get_page, 'https://www.python.org/')
    19 g2=gevent.spawn(get_page, 'https://www.yahoo.com/')
    20 g3=gevent.spawn(get_page, 'https://github.com/')
    21 
    22 gevent.joinall([g1,g2,g3])
    23 stop_time=time.time()
    24 print('run time is %s' %(stop_time-start_time))
    爬虫练习

    练习题2

     1 from gevent import monkey;monkey.patch_all()
     2 import gevent
     3 from socket import *
     4 def talk(conn,addr):
     5     while True:
     6         data=conn.recv(1024)
     7         print('%s:%s %s' %(addr[0],addr[1],data))
     8         conn.send(data.upper())
     9     conn.close()
    10 
    11 def server(ip,port):
    12     s = socket(AF_INET, SOCK_STREAM)
    13     s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    14     s.bind((ip,port))
    15     s.listen(5)
    16     while True:
    17         conn,addr=s.accept()
    18         gevent.spawn(talk,conn,addr)
    19     s.close()
    20 
    21 if __name__ == '__main__':
    22     server('127.0.0.1', 8088)
    并发通信 服务端
     1 from multiprocessing import Process
     2 from socket import *
     3 def client(server_ip,server_port):
     4     client=socket(AF_INET,SOCK_STREAM)
     5     client.connect((server_ip,server_port))
     6     while True:
     7         cmd=input('')
     8         client.send(cmd.encode('utf-8'))
     9         msg=client.recv(1024)
    10         print(msg.decode('utf-8'))
    11 
    12 if __name__ == '__main__':
    13     client('127.0.0.1', 8088)
    并发通信 客户端
  • 相关阅读:
    Adding Swap Files
    Creating a Swap Partition
    linux删除或隐藏命令历史记录history
    Unix系统解压tar包时出现@LongLink错误
    【白话经典算法系列之十七】 数组中只出现一次的数 其他三次
    MapReduce原理及其主要实现平台分析
    二叉搜索树转换为有序双向链表
    实现O(1)获取最大最小值的栈----java
    对线性回归,logistic回归和一般回归的认识
    SMO优化算法(Sequential minimal optimization)
  • 原文地址:https://www.cnblogs.com/liuwei0824/p/7460168.html
Copyright © 2011-2022 走看看