协程

一.同步异步

  线程的三种状态:

    1.就绪

    2.运行

    3.阻塞

  阻塞 遇到了IO操作 代码卡住 无法执行 CPU会切换到其他任务

  非阻塞 与阻塞相反 代码正在执行(运行状态)或处于就绪状态

  阻塞和非阻塞秒回的是运行的状态

 

  同步:提交任务必须等待任务完成,才能执行下一行

  异步:提交任务不需要等待任务完成,立即执行下一行

  指的是一种提交任务方式

def task():
    for I in range(10000):
        I+=1000
    print("111111")

print("start")
task()#同步提交
print("en")

form threading import Thread

print("start1")
Thread(target=task).start()#异步提交
print("end")

二.异步回调

  为什么需要异步回调

    子进程帮助主进程完成任务 处理任务的结果应该还给主进程

  其他方式也可以将数据交给主进程

    1.shutdown 主进程会叨叨所有任务完成

    2.result函数 会阻塞知道任务完成

  注意:

    回调函数什么时候执行?子进程任务完成时

    谁在执行回调函数?主进程

  线程的异步回调

    使用方法相同 唯一不同是线程没有主线程,都是子线程执行

三.生产者消费者

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from threading import current_thread
pool = ThreadPoolExecutor()

import requests

def get_data_task(url):
    print(current_thread(),"正在生产数据!|")

    response =requests.get(url)
    text = response.content.decode("utf-8")
    print(text)
    return text

def parser_data(f):
    print(current_thread(),"处理数据")
    print("正在解析:长度%s"%len(f.result()))

urls=[
    "http://www.baidu.con",
    "http://www.taobao.con",
    "http://www.jindong.con",
    "http://www.4399.con"
]
if __name__=='__main__':
    for url in urls:
        f = pool.submit(get_data_task,url)
        f.add_done_callback(parser_data)
        print("over")

四.线程队列

import queue

# 普通队列 先进先出
q = queue.Queue()
q.put("a")
q.put("b")


print(q.get())
print(q.get())

# 堆栈队列  先进后出 后进先出  函数调用就是进栈  函数结束就出栈 递归造成栈溢出
q2 = queue.LifoQueue()
q2.put("a")
q2.put("b")
print(q2.get())


# 优先级队列
q3 = queue.PriorityQueue()  # 数值越小优先级越高  优先级相同时 比较大小 小的先取
q3.put((-100,"c"))
q3.put((1,"a"))
q3.put((100,"b"))
print(q3.get())

五.协程

  协程的目的是在单线程下实现并发
  为什么出现协程? 因为cpython 由于GIL 导致同一时间只有一个线程再跑
    意味着 如果你的程序时计算密集 多线程效率也不会提升
    如果是io密集型 有没有必要再单线程下实现并发
    没有 我会开启多线程来处理io 子线遇到io cpu切走 但是请问 你能保证一定切到主线吗? 不能保证
    有 如果可以 我在遇到io的时候转而去做计算 这样一来可以保证cpu一直在处理你的程序 当然时间太长也要切走

 

 

    总结一下:单线下实现并发 将io阻塞时间用于执行计算 可以提高效率 原理:一直使用CPU直到超时
  怎么实现单线程并发?
    并发 指的是 看起来像是同时运行 实际是在任务间来回切换 同时需要保存执行的状态
    任务一堆代码 可以用函数装起来
    1.如何让两个函数切换执行
      yield可以保存函数的执行状态
      通过生成器可以实现伪并发
      并发不一定提升效率 反而会降低效率 当任务全是计算时
    2.如何知道发生了io? 从而切换执行
      目前咱们实现不了
  第三方模块 greenlet 可以实现并发 但是不能检测io
  第三方模块 gevent 封装greenlet 可以实现单线程并发 并且能够检测io操作 自动切换

  协程的应用场景:
    TCP 多客户端实现方式
    1.来一个客户端就来一个进程 资源消耗较大
    2.来一个客户端就来一个线程 也不能无限开
    3.用进程池 或 线程池 还是一个线程或进程只能维护一个连接
    4.协程 一个线程就可以处理多个客户端 遇到io就切到另一个

 六 greenlet

如果我们在单个线程内有20个任务,要想实现在多个任务之间切换,使用yield生成器的方式过于麻烦(需要先得到初始化一次的生成器,然后再调用send。。。非常麻烦),而使用greenlet模块可以非常简单地实现这20个任务直接的切换

from greenlet import greenlet

def eat(name):
    print('%s eat 1' %name)
    g2.switch('egon')
    print('%s eat 2' %name)
    g2.switch()
def play(name):
    print('%s play 1' %name)
    g1.switch()
    print('%s play 2' %name)

g1=greenlet(eat)
g2=greenlet(play)

g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要

单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度

 

greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。

单线程里的这20个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率,这就用到了Gevent模块。

 

七 gevent

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

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的返回值

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

import gevent
def eat(name):
    print('%s eat 1' %name)
    gevent.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    gevent.sleep(1)
    print('%s play 2' %name)


g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('')

上例gevent.sleep(2)模拟的是gevent可以识别的io阻塞,

而time.sleep(2)或其他的阻塞,gevent是不能直接识别的需要用下面一行代码,打补丁,就可以识别了

from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前

或者我们干脆记忆成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头

 

from gevent import monkey;monkey.patch_all()

import gevent
import time
def eat():
print('eat food 1')
time.sleep(2)
print('eat food 2')

def play():
print('play 1')
time.sleep(1)
print('play 2')

g1=gevent.spawn(eat)
g2=gevent.spawn(play_phone)
gevent.joinall([g1,g2])
print('主')

 

什么是协程

协程是是\单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
为什么需要协程

因为线程是从内核级别进行调度,当某个线程遇到IO或执行时间过长时,操作系统会强制切换到其他线程

而在单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率


协程的使用场景


TCP 多客户端实现方式
1.来一个客户端就来一个进程 资源消耗较大
2.来一个客户端就来一个线程 也不能无限开
3.用进程池 或 线程池 还是一个线程或进程只能维护一个连接
4.协程 一个线程就可以处理多个客户端 遇到io就切到另一个


2.使用协程完成TCP套接字编程 支持多客户端同时访问


3.什么是异步 什么是异步回调 为什么需要异步回调

异步 :提交任务不需要等待任务完成,立即执行下一行

子进程帮助主进程完成任务 处理任务额结果应该还给主进程

提高效率,让每一个线程都工做起来.

 

posted @ 2018-11-13 21:28  Milford  阅读(173)  评论(0编辑  收藏  举报