Twenty-nine 协程

 

1、内容回顾

  进程:

    数据隔离

    开启销毁切换开销大

    multiprocessing

      Process 开进程

      Lock 保证数据安全

      Queue IPC机制

      Manager 数据共享类

      Pool 池

  线程:

    数据共享

    开启销毁切换开销小

    线程是进程内部的一个执行单位

    threading

      Thread 开线程

      Lock 在同一线程中只能acquire 一次

      Rlock 在同一线程中可以联系acquire多次

    queue

      队列 先进先出

      栈 后进先出

      优先级队列 优先级高的先出

    concurrent.futrues

      进程池  ProcessPoolExecutor

        CPU+1

      线程池  ThreadPoolExecutor

        CPU*5    

2、协程

  a、协程的本质 就是一条线程分成多份,每一份去执行一段代码

      多段代码在一条线程内来回切换

  b、如果一段代码遇到阻塞的过程中切换到另一段可以执行的代码上

      就相当于完成利用协程完成了更加充分利用线程的目的

  c、协程利用切换来规避IO操作带来的好处

      a)一条线程能够执行多个任务

      b)减少一条线程的阻塞,帮助线程在操作系统调度的时候多抢占CPU

      c)协程由于是操作系统不可见,所以协程的切换不是由操作系统控制的

        是由程序员控制的,用户级

      d)协程之间永远数据安全-因为协程的本质是一条线程

3、用yield完成的协程函数

# 协程
# 在两个任务之间来回切换
import time
def pro():
    print(1)
    n = yield 'a'
    time.sleep(1)
    print(n)
    yield 'b'

def com():
    g = pro()
    a = next(g)
    print(a)
    b = g.send(2)
    print(b)

com()

3、第三方模块安装方式:cmd或者pycharm

  pytharm中安装:

    不需要考虑pip、命令

    你的计算机可能由多个python版本

    一定保证安装在当前环境下之间使用

  以后我们的开发环境、测试环境和生产环节

    开发环境:你写代码用的环境

    测试环境:测试人员用的环境

    生产环境:你写代码实际对用户提供服务的时候所用的环境

  cmd中安装

    确认有pip.exe文件

    pip.install 模块名 帮助我们完成模块以及其依赖模块的安装

4、greenlet底层模块(做状态的切换) gevent上层模块(做规避IO的切换)

from gevent import monkey
monkey.patch_all()    # 这句话之后导入模块的IO都会打成一个包,如果后面执行的IO模块跟包相关,就是IO操作
import time
import gevent

def eat():
    print('eat1')    #
    time.sleep(1)    # 也有IO操作,但是没有进行切换,因为不认识,所以要monkey
    print('eat2')

def sleep():
    print('sleep1')
    time.sleep(1)
    print('sleep2')

g1 = gevent.spawn(eat)  # 告诉电脑是协程任务,但只有遇到阻塞事件才会执行
g2 = gevent.spawn(sleep)
gevent.joinall([g1,g2])    # IO阻塞等待终止,精准预估到IO多久,time因为monkey相当于IO操作

5、爬虫的应用

def func(name,url):
    ret = request.urlopen(url)
    with open(name+'.html','wb') as f:
        f.write(ret.read())
url_lst = [
    ('python','https://www.python.org/'),
    ('blog','http://www.cnblogs.com/Eva-J/articles/8324673.html'),
    ('pypi','https://pypi.org/project/pip/'),
    ('blog2','https://www.cnblogs.com/z-x-y/p/9237706.html'),
    ('douban','https://www.douban.com/')
]
start = time.time()
for url_item in url_lst:
    func(*url_item)
end = time.time()
print(end - start)

from gevent import monkey
monkey.patch_all()
import gevent
import time
from urllib import request

def func(name,url):
    ret = request.urlopen(url)
    with open(name+'2.html','wb') as f:
        f.write(ret.read())

start = time.time()
g_l = []
for url_item in url_lst:
    g = gevent.spawn(func,*url_item)
    g_l.append(g)
gevent.joinall(g_l)
end = time.time()
print(end - start)

6、并发一个协程server

from gevent import monkey
monkey.patch_all()
import socket
import gevent
def talk(conn):
    while True:
        msg = conn.recv(1024).decode('utf-8')
        conn.send(msg.upper().encode('utf-8'))

sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
while True:
    conn,addr = sk.accept()
    gevent.spawn(talk,conn)

# 500个协程 一条线程 顶 500个线程的服务
# 进程的个数 CPU+1
# 线程个数   CPU*5
# 协程个数   500
# 5*20=100 * 500 =50000

 7、事件:两个事情,需要一个事情确认一下另一个事情才能开始做

#如访问数据库的模拟连接代码
from threading import Event
# 事件
# wait() 阻塞 到事件内部标识为True就停止阻塞
# 控制标识
    # set
    # clear
    # is_set

# 连接数据库
import time
import random
from threading import Thread,Event
def connect_sql(e):
    count = 0
    while count < 3:    #连接3次
        e.wait(0.5)    #每次连接时间为0.5秒
        if e.is_set():
            print('连接数据库成功')    
            break
        else:
            print('数据库未连接成功')
            count += 1

def test(e):
    time.sleep(random.randint(0,3))    #随机连接事件
    e.set()

e = Event()
Thread(target=test,args=(e,)).start()
Thread(target=connect_sql,args=(e,)).start()

 

posted @ 2019-01-30 11:08  pythonernoob  阅读(129)  评论(0)    收藏  举报