Python3 并发编程4

二次信任 提交于 2019-12-02 06:56:13

Event事件

  • 用来控制线程的执行
  • e.isSet()查看对象e当前的信号状态, 默认为False
  • e.wait() 信号状态为False, 则当前线程阻塞
  • e.set() 将e的信号状态设置为True, 被阻塞的线程进入非阻塞状态
from threading import Thread
from threading import Event
import time

e = Event()


def light():
    print('*********红灯!**********')
    print(f'对象当前的信号状态为{e.isSet()}')
    time.sleep(5)
    print('*********绿灯!**********')
    e.set()  # 将e的信号标准设为True
    print(f'对象当前的信号状态为{e.isSet()}')


def driver(name):
    print(f'{name}正在等红灯!')
    e.wait()  # 如果e信号标志为False, 则当前线程阻塞
    print(f'{name}弹射起步!')


if __name__ == '__main__':

    t1 = Thread(target=light)
    t1.start()

    for i in range(10):
        t2 = Thread(target=driver, args=(f'老司机{i+1}号',))
        t2.start()

        
'''
**********红灯!**********
对象当前的信号状态为False
老司机1号正在等红灯!
老司机2号正在等红灯!
老司机3号正在等红灯!
老司机4号正在等红灯!
老司机5号正在等红灯!
老司机6号正在等红灯!
老司机7号正在等红灯!
老司机8号正在等红灯!
老司机9号正在等红灯!
老司机10号正在等红灯!
**********绿灯!**********
对象当前的信号状态为True
老司机1号弹射起步!
老司机5号弹射起步!
老司机7号弹射起步!
老司机8号弹射起步!
老司机9号弹射起步!
老司机6号弹射起步!
老司机4号弹射起步!
老司机2号弹射起步!
老司机10号弹射起步!
老司机3号弹射起步!
'''

线程池与进程池

基本概念

  • 用来控制当前程序允许创建进程/线程的数量
  • 防止程序创建的进程/线程过多, 超过硬件承受的范围

使用方法

  • pool = ProcessPoolExecutor(5) 当前任务最多只能同时开启5个进程, 默认线程个数是CPU个数
  • pool = ThreadPoolExecutor(5) 当前任务最多只能同时开启5个线程, 默认线程个数是CPU个数 * 5
  • pool.submit(函数地址, 参数) 提交任务
  • pool.submit(函数地址, 参数).add_done_callback(回调函数地址) 提交任务, 并把任务的返回值传给回调函数
  • pool.shutdown() 让线程池任务都执行完后再往下执行代码
from concurrent.futures import ThreadPoolExecutor
import time

pool = ThreadPoolExecutor(5)


# 送快递
def deliver(goods):
    print(f'{goods}开始发货!')
    time.sleep(1)
    print(f'{goods}已经签收!')
    return True


# 拿快递(回调函数)
def get_goods(res):
    get = res.result()
    if get:
        print('开始拆快递!')
    else:
        print('投诉!')


for i in range(5):
    pool.submit(deliver, f'格子衬衫牛仔裤{i+1}').add_done_callback(get_goods)

# 让线程池中的线程运行完毕再执行下面的代码
pool.shutdown()
print('钱包空空!')


'''
格子衬衫牛仔裤1开始发货!
格子衬衫牛仔裤2开始发货!
格子衬衫牛仔裤3开始发货!
格子衬衫牛仔裤4开始发货!
格子衬衫牛仔裤5开始发货!
格子衬衫牛仔裤1已经签收!
开始拆快递!
格子衬衫牛仔裤2已经签收!
开始拆快递!
格子衬衫牛仔裤5已经签收!
开始拆快递!
格子衬衫牛仔裤4已经签收!
开始拆快递!
格子衬衫牛仔裤3已经签收!
开始拆快递!
钱包空空!
'''

当我们想开启10个线程时, 既for i in range(10):, 结果如下

'''
格子衬衫牛仔裤1开始发货!
格子衬衫牛仔裤2开始发货!
格子衬衫牛仔裤3开始发货!
格子衬衫牛仔裤4开始发货!
格子衬衫牛仔裤5开始发货!
格子衬衫牛仔裤2已经签收!
开始拆快递!
格子衬衫牛仔裤6开始发货!
格子衬衫牛仔裤1已经签收!
开始拆快递!
格子衬衫牛仔裤7开始发货!
格子衬衫牛仔裤4已经签收!
格子衬衫牛仔裤3已经签收!
开始拆快递!
格子衬衫牛仔裤8开始发货!
格子衬衫牛仔裤5已经签收!
开始拆快递!
格子衬衫牛仔裤9开始发货!
开始拆快递!
格子衬衫牛仔裤10开始发货!
格子衬衫牛仔裤6已经签收!
开始拆快递!
格子衬衫牛仔裤10已经签收!
开始拆快递!
格子衬衫牛仔裤7已经签收!
开始拆快递!
格子衬衫牛仔裤8已经签收!
开始拆快递!
格子衬衫牛仔裤9已经签收!
开始拆快递!
钱包空空!
'''

和信号量的区别

  • 信号量: 工作线程是我们自己创建的, 需要我们手动进行限流
  • 线程池: 工作线程是线程池创建的, 线程池自动限流

协程(coroutine)

基本概念

  • 在单线程下实行并发(切换 + 保存)
  • 线程是系统级别的, 由操作系统调度. 协程是程序级别的, 需要程序员自己调度
  • 优点: 不需要上下文切换的开销, 节省空间和时间
  • 缺点: 无法利用多核优势, 进行阻塞操作会阻塞整个程序

实现方式

  • yield实现
import time


# 生成器
def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print(f'[CONSUMER] consuming {n}...')
        time.sleep(1)
        r = '200 OK'


def producer(c):
    c.__next__()  # 初始化生成器
    n = 0
    while n < 5:
        n = n + 1
        print(f'[PRODUCER] producing {n}...')
        r = c.send(n) # 切换到consumer执行
        print(f'[PRODUCER] consumer return: {r}')
    c.close()


if __name__ == '__main__':
    # c是生成器对象
    c = consumer()
    producer(c)
    
'''
[PRODUCER] producing 1...
[CONSUMER] consuming 1...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 2...
[CONSUMER] consuming 2...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 3...
[CONSUMER] consuming 3...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 4...
[CONSUMER] consuming 4...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 5...
[CONSUMER] consuming 5...
[PRODUCER] consumer return: 200 OK
'''
  • gevent模块实现
from gevent import monkey;
monkey.patch_all()  # 猴子补丁, 修改Python一些标准库
from gevent import spawn, joinall
import time


def func1():
    print('1')
    time.sleep(1)


def func2():
    print('2')
    time.sleep(2)


def func3():
    print('3')
    time.sleep(3)


start_time = time.time()

s1 = spawn(func1)
s2 = spawn(func2)
s3 = spawn(func3)

joinall([s1, s2, s3])

end_time = time.time()

print(end_time - start_time)


'''
1
2
3
3.007172107696533
'''

高性能爬取梨视频

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!