• day 31协程


    Event事件

    作用:用来控制线程的执行,由一些线程去控制另一些线程。

    from threading import Event
    from threading import Thread
    import time
    
    #调用Event类实例化一个对象
    e=Event()
    
    #若是该方法出现在任务中,则为False,阻塞
    # e.wait()#False
    
    #若该方法出现在任务中,则将其他线程的False改为True,进入就绪态与运行态
    # e.set()#True
    
    def light():
        print('红灯亮...')
        time.sleep(5)
        #应该开始发送信号,告诉其他线程准备执行
        e.set()#将car中的False变成True
        print('绿灯亮')
    
    def car(name):
        print(f'{name}正在等待红灯。。。')
        #让所有汽车任务进入阻塞态
        e.wait()#False
        print(f'{name}开始飘逸')
    
    #让一个light线程任务,控制多个car线程任务
    t=Thread(target=light)
    t.start()
    for line in range(10):
        t=Thread(target=car,args=(f'童子军坦克{line}号',))
        t.start()
    '''红灯亮...
    童子军坦克0号正在等待红灯。。。
    童子军坦克1号正在等待红灯。。。
    童子军坦克2号正在等待红灯。。。
    童子军坦克3号正在等待红灯。。。
    童子军坦克4号正在等待红灯。。。
    童子军坦克5号正在等待红灯。。。
    童子军坦克6号正在等待红灯。。。
    童子军坦克7号正在等待红灯。。。
    童子军坦克8号正在等待红灯。。。
    童子军坦克9号正在等待红灯。。。
    绿灯亮
    童子军坦克4号开始飘逸
    童子军坦克6号开始飘逸
    童子军坦克7号开始飘逸
    童子军坦克0号开始飘逸
    童子军坦克3号开始飘逸
    童子军坦克5号开始飘逸
    童子军坦克2号开始飘逸
    童子军坦克8号开始飘逸
    童子军坦克1号开始飘逸
    童子军坦克9号开始飘逸'''
    

    线程池与进程池

    ​ 1)什么是进程池与线程池

    ​ 进程池与线程池是用来控制当前程序允许(进程/线程)的数量。

    ​ 2)进程池与线程池的作用

    ​ 保证在硬件允许的范围内创建(进程/线程)的数量

    ​ 3)如何使用:

    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time
    
    ProcessPoolExecutor(5)#5代表只能开启5个进程
    ProcessPoolExecutor()#默认以CPU的个数限制进程数
    
    pool=ThreadPoolExecutor(5)#5代表只能开启5个线程
    ThreadPoolExecutor()#默认以CPU个数*5限制线程数
    
    #pool.submit('传函数地址')#异步提交任务
    # def task():
    #     print('线程任务开始了。。。')
    #     time.sleep(1)
    #     print('线程任务结束了')
    #
    # for line in range(5):
    #     pool.submit(task)
        #等同于t=Tread()  t.start()
    '''线程任务开始了。。。
    线程任务结束了
    线程任务开始了。。。
    线程任务结束了
    线程任务开始了。。。
    线程任务结束了
    线程任务开始了。。。
    线程任务结束了
    线程任务开始了。。。
    线程任务结束了'''
    
    
    #pool.submit('传函数地址').add_done_callback('回调函数地址')
    def task():
        print('线程任务开始了。。。。')
        time.sleep(5)
        print('线程任务结束了。。。')
        return 123
    
    #回调函数
    def call_back(res):
        print(type(res))
        #注意:赋值操作不要与接受的res同名
        res2=res.result()
        print(res2)
    
    for line in range(5):
        pool.submit(task).add_done_callback(call_back)
    '''线程任务开始了。。。。
    线程任务开始了。。。。
    线程任务开始了。。。。
    线程任务开始了。。。。
    线程任务开始了。。。。
    线程任务结束了。。。
    <class 'concurrent.futures._base.Future'>
    123
    线程任务结束了。。。
    <class 'concurrent.futures._base.Future'>
    123
    线程任务结束了。。。
    <class 'concurrent.futures._base.Future'>
    123
    线程任务结束了。。。
    线程任务结束了。。。
    <class 'concurrent.futures._base.Future'>
    123
    <class 'concurrent.futures._base.Future'>
    123'''
    
    
    

    高性能爬取梨视频

    '''
    1.从主页中获取所有的视频ID号(1615201,1614813)
        -拼接视频详情url
         https://www.pearvideo.com/video_ + '视频ID号'
    
    2.在视频详情页中获取真实视频Url
    
    3.往真实视频url地址发送请求获取 视频 二进制
    
    4.最后把视频二进制数据保存到本地
    '''
    from concurrent.futures import ThreadPoolExecutor
    import requests
    import re
    import uuid
    
    pool=ThreadPoolExecutor(200)
    # 1.发送请求函数
    def get_page(url):
        response=requests.get(url)
        return response
    
    #2.解析主页获取视频ID号
    def parse_index(response):
        id_list=re.findall('<a href="video_(.*?)".*?>',response.text,re.S)
        return id_list
    
    
    #3.解析视频详情页获取真实视频链接
    def parse_detail(res):
        response=res.result()
        movie_detail_url=re.findall('srcUrl="(.*?)"',response.text,re.S)[0]
        print(f'往视频链接:{movie_detail_url}发送请求')
        #异步往视频详情页链接发送请求,把结果交给
        pool.submit(get_page,movie_detail_url).add_done_callback(save_movie)
        return movie_detail_url
    
    #4.往真实视频链接发送请求,获取数据并保存到本地
    def save_movie(res):
        movie_response=res.result()
        name=str(uuid.uuid4())
        print(f'{name}视频开始保存')
        with open(f'{name}.mp4','wb') as f:
            f.write(movie_response.content)
        print('视频下载完毕')
    
    
    if __name__ == '__main__':
        index_response=get_page('https://www.pearvideo.com/')
        # print(index_response.text)
        id_list=parse_index(index_response)
        print(id_list)
        for id in id_list:
            detail_url='https://www.pearvideo.com/video_'+id
            pool.submit(get_page,detail_url).add_done_callback(parse_detail)
    

    协程

    -进程:资源单位

    -线程:执行单位

    -协程:在单线程下实现并发

    注意:协程不是操作系统资源,他是程序起的名字,为让单线程能实现并发

    协程的目的:

    ​ -操作系统:

    ​ 多道技术,切换+保存状态

    ​ 1)遇到IO

    ​ 2)CPU执行时间过长

    ​ -协程:

    ​ 通过手动模拟操作系统 “多道技术”,实现 切换+保存状态

    ​ 1)手动实现 遇到IO切换,七篇操作系统误以为没有IO操作

    ​ -单线程 遇到IO,切换+保存状态

    ​ -单线程 计算密集型,来回切换+保存状态 反而效率更低

    ​ 优点:在IO密集型的情况下,会提高效率

    ​ 缺点:若在计算密集型的情况下,来回切换+保存状态

    ​ -yield:保存状态

    ​ -并发:切换

    '''
    gevent:
        是一个第三方模块,可以帮你监听IO操作,并切换
    使用gevent目的:
        为了实现单线程下,实现遇到IO,保存状态+切换
    '''
    import time
    from gevent import spawn,joinall,monkey#用于切换+保存数据
    monkey.patch_all()#检测IO
    def func1():
        print('1')
        time.sleep(1)
    
    def func2():
        print('2')
        time.sleep(3)
    
    def func3():
        print('3')
        time.sleep(5)
     
    
    s1=spawn(func1)
    s2=spawn(func2)
    s3=spawn(func3)
    # s1.join()
    # s2.join()
    # s3.join()
    joinall([s3,s2,s1])
    '''1
    2
    3
    '''
    

    单协程实现TCP服务端并发

    # 服务端
    from gevent import monkey,spawn
    monkey.patch_all()#检测IO
    import socket
    
    server=socket.socket()
    server.bind(('127.0.0.1',9527))
    server.listen(5)
    print('启动服务端。。。')
    
    # 线程任务,执行接受客户端消息与发送端消息给客户
    def working(conn):
        while True:
            try:
                print('协程')
                data=conn.recv(1024).decode('utf-8')
                if len(data)==0:
                    break
                # print(data)
                send_data=data.upper().encode('utf-8')
                conn.send(send_data)
            except Exception as e:
                print(e)
                break
        conn.close()
    #
    # def server2():
    #     while True:
    #         conn,addr=server.accept()
    #         spawn(working,conn)
    #         # working(conn)
    # if __name__ == '__main__':
    #     s1=spawn(server2)
    #     s1.join()
    while True:
        conn, addr = server.accept()
        s1=spawn(working, conn)
        s1.join()
    
    # def server2():
    #     while True:
    #         conn,addr=server.accept()
    #         spawn(working,conn)
    #         # working(conn)
    # if __name__ == '__main__':
    #     # s1=spawn(server2)
    #     # s1.join()
    #     server2()
    
    #客户端
    import socket
    from threading import Thread,current_thread
    
    def client():
        client=socket.socket()
        client.connect(('127.0.0.1',9527))
        print('启动客户端。。。')
        number=0
        while True:
            send_data=f'{current_thread().name}{number}'
            client.send(send_data.encode('utf-8'))
            data=client.recv(1024)
            print(data.decode('utf-8'))
            number+=1
    
    for i in range(3):
        t=Thread(target=client)
        t.start()
    
    
  • 相关阅读:
    特征选择(四)- 分散度
    机器学习实践中的7种常见错误
    逻辑回归 vs 决策树 vs 支持向量机(I)
    逻辑回归 vs 决策树 vs 支持向量机(II)
    线性回归和逻辑回归
    Mac 下 python 环境问题
    Linux目录/usr结构说明
    Python 处理 json
    感知机、logistic回归 损失函数对比探讨
    SVM探讨
  • 原文地址:https://www.cnblogs.com/zqfzqf/p/11735600.html
Copyright © 2020-2023  润新知