协程

1.异步回调

使用场景:

爬虫(1.从目标站点下载网页数据 2.用re从字符串中提取你需要的数据)

什么式异步回调?

a 交给b一个任务, b在执行完成后回过头来调用了a的一个函数,称之为回调函数

通常异步任务都会和回调函数一起使用

使用方式: 通过add_done_callback() 给 future

为什么要用异步回调

需要获取异步任务的结果,但是又不应该阻塞(降低效率)

高效的获取任务结果

'''
1.从目标站点下载网页数据 本质就是HTML格式字符串
2.用re从字符串中提取出你需要的数据
'''
import requests, re, os
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from threading import current_thread
​
def get_data(url):
    print("%s 正在请求%s" % (current_thread().name, url))
    response = requests.get(url)
    print("%s 请求%s成功" % (current_thread().name, url))
    return response
def parser(obj):
    res = obj.result()
    htm = res.content.decode("utf-8")
    ls = re.findall("href=.*?com", htm)
    print("%s解析完成! 共%s个连接" % (current_thread().name,len(ls)))
if __name__ == '__main__':
    urls = ["https://www.baidu.com",
            "https://www.tmall.com",
            "https://www.taobao.com",
            "https://www.jd.com",
            "https://www.python.org",
            "https://www.apple.com"]
    pool = ThreadPoolExecutor(3)
    for i in urls:
        obj = pool.submit(get_data, i)
        obj.add_done_callback(parser)
异步回调案例:爬虫>>>

2.线程队列

线程队列的本身就是一个普通容器,不能被进程共享

线程队列与进程队列的却别:

进程队列可以被多进程共享,而线程队列则本身就是一个普通容器,不能被进程共

先进先出队列:Queue


q = Queue(1)
q.put("a")
q.put("b",timeout=1)
​
print(q.get())
print(q.get(timeout=2))

后进先出队列: LifoQueue


lq = LifoQueue()
lq.put("a")
lq.put("b")
lq.put("c")
​
print(lq.get())
print(lq.get())
print(lq.get())

优先级队列: PriorityQueue()

取出顺序式是,由小到大,优先级可以是数字或字符,只要能够比较大小即可


pq = PriorityQueue()
​
pq.put((["a"],"bdslkfjdsfjd"))
pq.put((["b"],"csdlkjfksdjkfds"))
pq.put((["c"],"asd;kjfksdjfkdsf"))
​
print(pq.get())
print(pq.get())
print(pq.get())
线程队列

3.线程事件

什么是事件? 是一种通知服务,用于协调多个线程工作,当一个线程需要执行某个操作,需要获取另外一个线程的状态时的通知服务就是事件
'''
 以TCP服务器与客户端连接为例
'''
import time
from threading import Thread
from threading import Event
​
# 创建一个事件
e = Event() #默认False
def start():
    print("正在启动服务器......")
    time.sleep(5)
    print("服务器启动成功!")
    e.set() # 就是把事件的值设置为True
def connect():
    # 重试3次
    for i in range(3):
        print("等待服务器启动....")
        e.wait(1) # 会阻塞 直到对方把事件设置为True
        if e.isSet():
            print("连接成功!")
            break
        else:
            print("连接失败")
    else: #如果3次都没成功 就打印这个消息
        print("服务器没有启动")
        
​
Thread(target=start).start()
Thread(target=connect).start()
以TCP服务器与客户端连接为例

4.单线程下实现并发效果

首先要明确定义:

      并发:指的是多个任务同时发生,看起来好像是同时都在进行

      并行: 指的是多个任务真正的同时进行

单线程下通过生成器完成并发>>>生成器的特点是:只要函数中出现yield该函数就变成了生成器

单线程并发是为了提高效率,对于计算密集类型任务,单线程并发会降低效率

对于IO密集型的任务,单线程并发会提高效率(在执行IO操作的时候,切换到其他计算任务,提高CPU的占用率)

import time
def task1():
    a = 1
    while True:
        print("task1 run")
        a += 1 
        print(a)
        yield
def task2():
    g = task1()
    while True:
        print('task2 run')
        time.sleep(10)
        next(g)
task2()


'''
  对于计算型任务 单线程并发效率较低
'''
import time
# def task1():
#     a = 0
#     for i in range(10000000):
#         a += i
#         yield
#
# def task2():
#     b = 0
#     g = task1()
#     for i in range(10000000):
#         b += i
#         next(g)
# s = time.time()
# task2()
# print(time.time()-s)
def task1():
    a = 0
    for i in range(10000000):
        a += i
def task2():
    b = 0
    for i in range(10000000):
        b += i
s = time.time()
task1()
task2()
print(time.time()-s)
单线程下的并发效果

5.greenlet

greenlet主要封装了生成器,使得在使用生成器是实现并发时,简化代码

该模块简化了yield复杂的代码结构,实现了单线程下多任务并发,但是无论直接使用yield还是greenlet都不能检测IO操作,遇到IO时同样进入阻塞状态,所以此时的并发是没有任何意义的。

import greenlet
import time
​
def task1():
    print('task1 run')
    time.sleep(10)
    g2.switch()
    print('task1 run')
def task2():
    print('task2 run')
    g1.switch()
​
g1 = greenlet.greenlet(task1)
g2 = greenlet.greenlet(task2)
g1.switch()
greenlet

6.协程

什么是协程?

协程是单线程下的并发,又称微线程,纤程

协程可以理解为提高线程工作效率,本质是单线程并发

协程也称之为微线程(它比线程更轻量级 单线程下任务的切换 比操作系统切换线程要简单的多)

为什么要有协程?

因为CPython中无法实现并行执行任务,导致效率降低,需要一种方法将效率最大化

协程的优点:

  1.协程的切换开销更小,属于程序级别的切换,操作系统完全能感知不到,因而更加轻量级

  2.单线程内就可以实现并发的效果,最大限度利用CPU

协程的缺点:

  1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程

  2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

怎么使用?

Python中 使用Gevent模块来 实现协程 其能在多个任务间进行切换 而且能够自己检测IO

from gevent import monkey
monkey.patch_all()
import gevent
import time
def task1():
    print("task1 run")
    time.sleep(10)
    print("task1 run")
def task2():
    print("task2 run")
    print("task2 run")
g1 = gevent.spawn(task1)
g2 = gevent.spawn(task2)
​
# g1.join()
# g2.join()
gevent.joinall([g1,g2])
协程使用
posted @ 2019-01-04 15:57  yyf1  阅读(209)  评论(0编辑  收藏  举报