• 协程


    直接使用线程或者进程发起异步

    from multiprocessing import Process
    from threading import Thread
    
    import time
    
    
    def task():
        time.sleep(2)
        print('run...')
    
    
    # 线程
    # p = Thread(target=task)
    # p.start()
    # print('over')
    # 进程
    if __name__ == '__main__':
        p = Process(target=task)
        p.start()
        print('over')
    直接使用线程或者进程发起异步

    获取异步任务结果的方式

      爬虫:

        1.获取到HTML文档

        2.从文档中取出需要的数据

      回调:其实说的是回调函数

         给异步任务绑定一个函数,当任务完成时会自动调用该函数

      具体使用:

        当你往pool中添加了一个异步任务,会返回一个表示结果的对象

        有一个对象绑定方法 add_done_callback 需要一个函数做为参数

        注意:回调函数 必须有且只有一个参数 就是对象本身

          通过对象点(.)result来获取结果

      优点:不用原地等待,任务结果可以立即获取到

    import requests
    from concurrent.futures import ThreadPoolExecutor
    import threading
    
    
    def get_data(url):
        res = requests.get(url)
        # print(res.text)
        print('%s正在处理%s' % (threading.current_thread().name, url))
        # 方式3 直接调用处理函数 生产者和消费者 强行耦合在一起
        # parser_data(res.text)
        return res.text, url
    
    
    def parser_data(f):
        # print(f)
        # res = f.result()
        print('解析长度为:%s,地址为:%s' % (f.result()[0], f.result()[1]))
    
    
    urls = ['https://www.baidu.com/', 'https://www.bilibili.com/', 'http://hs.blizzard.cn/landing']
    pool = ThreadPoolExecutor()
    fs = []
    for url in urls:
        f = pool.submit(get_data, url)  # 提交任务
        f.add_done_callback(parser_data)
        # 方式一 把并发变成了串行
        # data = f.result()
        # parser_data(data)
        # fs.append(f)
    # 方式2必须等待全部完成才返回结果
    # pool.shutdown()
    # for f in fs:
    #     data = f.result()
    #     parser_data(data[0], data[1])
    异步回调

    如何为线程或进程绑定回调函数

    from threading import Thread
    from concurrent.futures import ThreadPoolExecutor
    import os
    
    
    #
    #
    # def task(callback):
    #     print('run ...')
    #     callback()
    #
    #
    # def callback():
    #     print('这是回调函数。。。')
    #
    #
    # t = Thread(target=task, args=(callback,))
    # t.start()
    def task():
        print('task...run')
    
    
    def call_back(obj):
        print(os.getpid())
        print(obj.result())
    
    
    print('主:', os.getpid())
    pool = ThreadPoolExecutor()
    f = pool.submit(task)
    f.add_done_callback(call_back)
    线程回调
    from multiprocessing import Process
    from concurrent.futures import ProcessPoolExecutor
    import os
    
    
    # def task(callback):
    #     print('run ...')
    #     callback()
    #
    #
    # def callback():
    #     print('这是回调函数。。。')
    #
    #
    # if __name__ == '__main__':
    #     t = Process(target=task, args=(callback,))
    #     t.start()
    
    
    def task():
        print('task...run')
    
    
    def call_back(obj):
        print(os.getpid())
        print(obj.result())
    
    
    if __name__ == '__main__':
        print('主:', os.getpid())
        pool = ProcessPoolExecutor()
        f = pool.submit(task)
        f.add_done_callback(call_back)
    进程回调

    线程队列

    queue该模块下提供了一些常见的数据容器但是它们仅仅是容器 每一个都有数据共享这个特点
    # from  multiprocessing import Queue
    from queue import Queue, LifoQueue, PriorityQueue
    
    # q=Queue()
    # q.put(1)
    # q.put(2)
    # print(q.get())
    # print(q.get())
    # print(q.get(timeout=2))
    
    # 后进先出队列(堆栈)
    # q = LifoQueue()
    # q.put(1)
    # q.put(2)
    # print(q.get())
    
    # 优先级队列
    # 需要传入一个元组类型 第一个是优先级 第二个是值
    q = PriorityQueue()
    # q.put((-111111, "abc"))
    # q.put((2, "abc"))
    q.put(("a", "hello world1"))
    q.put(("A", "hello world2"))
    q.put(("c", "hello world"))
    
    print(q.get())
    线程队列

    事件 美国911事件 圆明园事件 

      事件是一个通知信息,表示什么时间发生了什么事情

      用于线程间通讯

      线程间本来就是数据共享的 也就是说 即使没有事件这个东西 也是没问题的

      线程之间,执行流程是完全独立的,一些时候可能需要知道另一个线程发生了什么 然后采取一些行动

      这个时候就可以使用事件来简化代码

      事件其实就是帮你维护了一个bool值

      在bool为True之前 wait函数将一直阻塞,这样一来就避免了不断的询问对方的状态

      假设 有两条线程 一个用于开启服务器 一个用于连接服务器

      连接服务器一定要保证服务器已经启动成功了,服务器启动需要花费一些时间

    import time, random
    
    from threading import Thread, Event
    
    # 使用事件
    # 一个事件
    boot = Event()
    
    
    def boot_server():
        print('正在启动服务器。。。')
        time.sleep(random.randint(2, 5))
        print('服务器启动成功!')
        boot.set()
    
    
    def connect_server():
        print('开始尝试连接。。。')
        boot.wait()  # 是一个阻塞函数 会一直等到set()函数被调用
        print('服务器连接成功!')
    
    # 不使用事件
    # 默认未启动
    # is_boot = False
    # 
    # 
    # def boot_server():
    #     global is_boot
    #     print('正在启动服务器。。。')
    #     time.sleep(random.randint(2, 5))
    #     print('服务器启动成功!')
    #     #   修改状态为True
    #     is_boot = True
    # 
    # 
    # def connect_server():
    #     while True:
    #         if is_boot:
    #             print('连接服务器成功!')
    #             break
    #         else:
    #             print('连接失败 服务器未启动!')
    #             time.sleep(1)
    # 
    # 
    # t1 = Thread(target=boot_server)
    # t1.start()
    # t2 = Thread(target=connect_server)
    # t2.start()
    事件

    协程

      协程就是要用单线程实现并发

      GIL导致多个线程不能并行,效率低

      CPython中多个线程不能并行

      既然多个线程也无法提高执行效率 还有没有必须要开线程

      例子:只有一个岗位 但是有十个任务,请十个人也提高不了效率,反而增加了系统开销

      现在只有一个线程,那要如何并发的处理十个任务

      一个线程如何能并发

        多道技术

        切换+保存状态

        首先任务其实就是一堆代码,一堆代码可以组成一个函数

        如何能使得可以在多个函数之间进行切换

      协程指的就是一个线程完成并发执行

      在CPyrhon中 如果你的任务是计算密集型 使用协程是无法提高效率的 反而因为切换任务导致效率降低

        只能靠进程了

      IO密集型 多线程 会比多进程效率高 因为线程的开销比进程小很多

      本质上协程还是只有一个线程 所以一旦遇到IO操作 整个线程就卡住了

      协程仅在以下场景能够提高效率

        1.任务是IO密集型

        2.一定要可以检测IO操作 并且在IO即将阻塞时 切换到计算任务 从而使得CPU尽可能 多得执行你的线程

    import time
    
    
    # def task1():
    #     for i in range(100000):
    #         time.sleep(10000)
    #         yield
    #
    #
    # def task2():
    #     g = task1()
    #     for i in range(100000):
    #         next(g)
    #
    #
    # start_time = time.time()
    # task2()
    # print(time.time() - start_time)
    
    def task1():
        for i in range(100000):
            1 + 1
    
    
    def task2():
        for i in range(100000):
            1 + 1
    
    
    start_time = time.time()
    task1()
    task2()
    print(time.time() - start_time)
    协程
    greenlet  
    greenlet是对yield进行
      简化了书写但是它不能检测IO操作
    import greenlet
    
    import time
    
    
    def task1():
        print('task1 run...')
        time.sleep(20)
        g2.switch()
        print('task1 over...')
    
    
    def task2():
        print('task2 run...')
        print('task2 over...')
        g1.switch()
    
    
    g1 = greenlet.greenlet(task1)
    g2 = greenlet.greenlet(task2)
    g1.switch()
    greenlet模块

    greenlet

      无法检测IO操作

      gevent既可单线程实现并发 又可以检测IO操作

    import gevent
    import gevent.monkey
    
    gevent.monkey.patch_all()
    # 打补丁的代码必须放在导入模块之前
    import time
    import threading
    
    
    def task1():
        # for i in range(1000):
        print('task1')
        print(threading.current_thread())
        time.sleep(1)
    
    
    def task2():
        # for i in range(1000):
        print(task2)
        print(threading.current_thread())
    
    
    g1 = gevent.spawn(task1)
    g2 = gevent.spawn(task2)
    
    # 协程创建完成后 必须调用join函数 否则任务不会开启 可以理解为线程中的start函数
    # g1.start()
    # g2.start()
    # g1.join()
    # g2.join()
    
    gevent.joinall([g1, g2])
    print('over')
    gevent
  • 相关阅读:
    微软Enterprise Library 4.0将支持依赖注入
    javascript cookies 存、取、删除实例
    动态调用 WebService(转)
    IE缓存是什么?cookies是什么?
    序列化
    PKI
    ASP.NET的(HttpModule,HttpHandler)(转)
    PKI基础 二.PKI基础5.数字证书及应用(转,一个加密解密的全过程)
    AOP技术基础(转)
    getChildByName()与getChildAt()效率比较
  • 原文地址:https://www.cnblogs.com/ShenJunHui6/p/10511982.html
Copyright © 2020-2023  润新知