day31

1、event事件

1.1 使用

模块:from threading import Event

实例化:e = Event()

方法:

e.wait(),线程任务调用了该方法时,该线程任务即会进入阻塞态

e.set(),线程任务调用了该方法时,其他的调用了e.wait()方法的线程任务即会从阻塞态进入就绪态、运行态

1.2 作用

控制线程的执行

2、线程池与进程池

2.1 什么是线程池、进程池

控制当前程序的允许创建的线程或进程数量

2.2 作用

保证在硬件允许的方位内创建线程或进程的数量

2.3 使用

模块:from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor

实例化:

pool = ThreadPoolExecutor(n) 无参时,默认为系统CPU数量的5倍

pool = ProcessPoolExcutor(n) 无参时,默认为系统CPU数量

方法:

pool.submit('函数对象'):异步提交任务,相当于实例化一个进程或线程并start()

pool.submit('函数对象').add_done_callback('回调函数对象'):将函数对象的返回值传给回调函数,并执行

pool.shutdown():让所有线程池的任务结束后,才往下执行代码

代码:

1、异步提交pool.submit('函数对象')

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time 
pool = ThreadPoolExecutor(5)

def task():
    print('线程开始执行任务')
    time.sleep(10)
    print('线程结束任务')
    
while True:
    pool.submit(task)

2、异步提交任务,并立即回调函数,参数为任务函数返回值

任务函数必须有返回值,回调函数拿到的是值的地址,需要通过.result()方法读取

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time
pool = ThreadPoolExecutor(5)

# 异步提交任务
def task():
    print('线程开始执行任务')
    time.sleep(10)
    print('线程结束任务')
    return 2
    
# 回调函数
def call_back(res):
    print(f'任务结果:{res}')
    # 注意的是,若在此处需要利用res进行赋值操作,那么不要与接收的res同名,因为在高并发时,可能会导致出现BUG
    res2 = res.result() + 2
    print(f'回调函数处理结果:{res2}')
    return 2
for i in range(10):
    pool.submit(task).add_done_callback(call_back)

pool.shutdown()
print('所有线程池的任务都结束了')

3、协程

3.1 进程、线程、协程的区别

进程:操作系统最小的资源单位

线程:操作系统最小的执行单位

携程:在单线程下实现并发,不是操作系统的资源单位

3.2 使用协程的目的

操作系统实现并发:

  • 多道技术,切换+保存状态
  • 遇到I/O时触发
  • 缺点是CPU执行时间过长

协程实现并发:

  • 通过手动模拟操作系统“多道技术”,实现切换+保存状态
  • 省略操作系统切换任务时的资源消耗,提高CPU执行效率

3.3 实现协程的方法

1、yield

2、第三方模块:gevent

gevent的作用:监听I/O操作,并自动实现切换任务

使用gevent的目的:为了实现单线程下,遇到I/O时,切换+保存任务

方法:

monkey.patch_all():可以监听该程序下所有的gevent可以识别的I/O操作,但是time.sleep()这样的I/O不能识别,因此本段代码就是一个补丁,将之写在 import time之前即可

spawn('函数对象',函数参数):执行对应的任务,并且可以在I/O时保存当前任务状态并切换到下一个任务

join():spawm()执行任务完成后,线程才会结束

joinall((函数对象,函数对象,)):等待所有的任务结束,才会结束线程

from gevent import spawn,joinall  # # 用于做切换 + 保存状态
from gevent import 	monkey

monkey.patch_all()  #  可以监听该程序下所有的I/O操作

def func1():
    print('1')
    # IO操作
    time.sleep(1)


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


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


start_time = time.time()

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

# s2.join()  # 发送信号,相当于等待自己 (在单线程的情况下)
# s1.join()
# s3.join()
# 必须传序列类型
joinall([s1, s2, s3])

end_time = time.time()

print(end_time - start_time)

4、多线程爬取梨视频

import requests
import re
from concurrent.futures import ThreadPoolExecutor
import uuid

pool = ThreadPoolExecutor(100)

# 主页
URL=r'https://www.pearvideo.com/'

def get_page(url):
    '''发送请求,获取网页信息'''
    response = requests.get(url)
    return response

def get_vedio_page_url_list(response):
    '''提取网页信息,获取所有视频信息页url'''
    re_format = '<a href="video_(.*?)".*?>'
    res = re.findall(re_format,response,re.S)
    for i in range(len(res)):
        res[i] = 'https://www.pearvideo.com/video_' + res[i]
    url_list = res
    return url_list

def get_video_data_url_list(video_page_url_list):
    '''通过视频信息也链接,发送请求并提取所有视频数据url'''
    video_data_url_list = []
    for url in video_page_url_list:
        response = get_page(url)
        print(response)
        re_format = 'srcUrl="(.*?)"'
        video_data_url = re.findall(re_format,response.text,re.S)[0]
        print(video_data_url)
        video_data_url_list.append(video_data_url)

    return video_data_url_list

def download(res):
    '''提取视频二进制数据,保存到本地'''
    res = res.result()  # 作为回调函数时使用

    name = str(uuid.uuid4())
    print(f'{name}.mp4视频开始保存...')
    with open(f'{name}.mp4','wb') as fw:
        fw.write(res.content)

if __name__ == '__main__':
    # 访问主页
    res = get_page(URL)
    
    #提取所有的视频详情页链接url
    video_page_url_list = get_vedio_page_url_list(res.text)
    
    # 提取所有的视频数据链接url
    video_data_url_list = get_video_data_url_list(video_page_url_list)
    
    # 多线程下载视频保存到本地
    for url in video_data_url_list:
        movie_data_url = pool.submit(get_page,url).add_done_callback(download)
    pool.shutdown(wait=True)
posted @ 2019-10-25 00:17  W文敏W  阅读(122)  评论(0)    收藏  举报