python-异步回调-线程队列-线程事件-greenlet-协程

一、异步回调

异步任务使用场景:

爬虫

  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) 
        #实现了并发请求数据,使用空闲线程解析数据

二、线程队列

  1. 线程队列与进程队列的区别:进程队列可以被多进程共享,而线程中的队列 就是一个普通的容器不能进程共享
from queue import Queue,LifoQueue,PriorityQueue
1.先进先出队列
q = Queue(1)
q.put('a')
print(q.get())
print(q.get(timeout=2))

2.后进先出队列
lq = LifoQueue()
lq.put('a')
lq.put('b')
lq.put('c')

print(lq.get()) #c
print(lq.get()) #b
print(lq.get()) #a

3.优先级队列 取出顺序是有小到大
pq = PriorityQueue()
pq.put((2,"b"))
pq.put((3,"c"))
pq.put((1,"a"))

print(pq.get()) #(1, 'a')
print(pq.get()) #(2, 'b')
print(pq.get()) #(3, 'c')


pq.put((["a"],"b"))
pq.put((["b"],"c"))
pq.put((["c"],"a"))

print(pq.get()) #(['a'], 'b')
print(pq.get()) #(['b'], 'c')
print(pq.get()) #(['c'], 'a')

三、线程事件

  1. 事件是用于协调多个线程工作的,当一个线程要执行某个操作,需要获取另一个线程的状态,你要给别人打电话必须明确知道对方手机买好了,作为客户端要连接服务器必须明确服务器已经启动了,那么作为启动服务器的一方,如何告知客户端?那么就通过事件

使用变量类完成多线程协作:

from threading import Thread
import time
is_boot = False
def start():
    global is_boot
    print('正在启动服务器')
    time.sleep(5)
    print('服务器启动成功')
    is_boot = True

def connect():
    while True:
        if is_boot:
            print('连接服务器成功')
            break
        else:
            print('连接服务器失败')
        time.sleep(0.5)

Thread(target=start).start()
Thread(target=connect).start()
from threading import Thread
from threading import Event
import time
#创建一个事件
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:
        print('服务器没有启动')

Thread(target=start).start()
Thread(target=connect).start()

四、在单线程下实现并发效果

  1. 通过生成器就能完成并发执行,生成器的特点只要函数中出现了yield该函数就变成了生成器

    使用生成器实现单线程并发

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(5)
        next(g)

task2()

单线程并发是为了提高效率,对于计算密集型任务,单线程并发反而降低效率,对于IO密集型,如果可以在执行IO操作的时候切换到其他计算任务,就能提高CPU占用率,从而提高效率

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) #耗时为2.8902969360351562
import time
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) #耗时为1.2698264122009277

五、greenlet

  1. greenlet 主要封装了生成器,使得我们在使用生成器实现并发时简化了代码
import greenlet
import time

def task1():
    print("task1 run")
    time.sleep(5)
    g2.switch()
    print("task1 run")

def task2():
    print("task2 run")
    g1.switch()

g1 = greenlet.greenlet(task1)
g2 = greenlet.greenlet(task2)
g1.switch()
#task1 run
#task2 run
#task1 run

六、协程

  1. 什么是协程:可以这么理解是协助线程更高效的工作,本质就是单线程实现并发,也称之为微线程(它比线程更轻量级,单线程下任务的切换比操作系统切换线程要简单的多)

  2. 为什么有协程:是因为在CPython中,无法并行执行任务,导致效率低,所以我们就需要一种方案能够将单线程的效率最大化就是协程。Python中使用Gevent模块来实现协程,其能在多个任务间进行切换,而且能够自己检测IO

from gevent import monkey
monkey.patch_all()

import gevent
import time

def task1():
    print('task1 run')
    time.sleep(5)
    print('task1 run')

def task2():
    print('task2 run')
    print('task2 run')

g1 = gevent.spawn(task1)
g2 = gevent.spawn(task2)

#g1.join() #等待任务执行结束
gevent.joinall([g1,g2]) #等待所有任务结束

#注意:如果开启了一个会产生的io的任务,并且你没有执行join,那么一旦发生io,这个任务就立马结束了

猜你喜欢

转载自blog.csdn.net/zdc45625/article/details/85792291
今日推荐