zoukankan      html  css  js  c++  java
  • 协程

    1.异步回调

    使用场景:

    爬虫(1.从目标站点下载网页数据 2.用re从字符串中提取你需要的数据)

    什么式异步回调?

    a 交给b一个任务, b在执行完成后回过头来调用了a的一个函数,称之为回调函数

    通常异步任务都会和回调函数一起使用

    使用方式: 通过add_done_callback() 给 future

    为什么要用异步回调

    需要获取异步任务的结果,但是又不应该阻塞(降低效率)

    高效的获取任务结果

    '''
    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)
    异步回调案例:爬虫>>>

    2.线程队列

    线程队列的本身就是一个普通容器,不能被进程共享

    线程队列与进程队列的却别:

    进程队列可以被多进程共享,而线程队列则本身就是一个普通容器,不能被进程共

    先进先出队列:Queue
    
    
    q = Queue(1)
    q.put("a")
    q.put("b",timeout=1)
    ​
    print(q.get())
    print(q.get(timeout=2))
    
    后进先出队列: LifoQueue
    
    
    lq = LifoQueue()
    lq.put("a")
    lq.put("b")
    lq.put("c")
    ​
    print(lq.get())
    print(lq.get())
    print(lq.get())
    
    优先级队列: PriorityQueue()
    
    取出顺序式是,由小到大,优先级可以是数字或字符,只要能够比较大小即可
    
    
    pq = PriorityQueue()
    ​
    pq.put((["a"],"bdslkfjdsfjd"))
    pq.put((["b"],"csdlkjfksdjkfds"))
    pq.put((["c"],"asd;kjfksdjfkdsf"))
    ​
    print(pq.get())
    print(pq.get())
    print(pq.get())
    线程队列

    3.线程事件

    什么是事件? 是一种通知服务,用于协调多个线程工作,当一个线程需要执行某个操作,需要获取另外一个线程的状态时的通知服务就是事件
    '''
     以TCP服务器与客户端连接为例
    '''
    import time
    from threading import Thread
    from threading import Event
    ​
    # 创建一个事件
    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: #如果3次都没成功 就打印这个消息
            print("服务器没有启动")
            
    ​
    Thread(target=start).start()
    Thread(target=connect).start()
    以TCP服务器与客户端连接为例

    4.单线程下实现并发效果

    首先要明确定义:

          并发:指的是多个任务同时发生,看起来好像是同时都在进行

          并行: 指的是多个任务真正的同时进行

    单线程下通过生成器完成并发>>>生成器的特点是:只要函数中出现yield该函数就变成了生成器

    单线程并发是为了提高效率,对于计算密集类型任务,单线程并发会降低效率

    对于IO密集型的任务,单线程并发会提高效率(在执行IO操作的时候,切换到其他计算任务,提高CPU的占用率)

    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(10)
            next(g)
    task2()
    
    
    '''
      对于计算型任务 单线程并发效率较低
    '''
    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)
    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)
    单线程下的并发效果

    5.greenlet

    greenlet主要封装了生成器,使得在使用生成器是实现并发时,简化代码

    该模块简化了yield复杂的代码结构,实现了单线程下多任务并发,但是无论直接使用yield还是greenlet都不能检测IO操作,遇到IO时同样进入阻塞状态,所以此时的并发是没有任何意义的。

    import greenlet
    import time
    ​
    def task1():
        print('task1 run')
        time.sleep(10)
        g2.switch()
        print('task1 run')
    def task2():
        print('task2 run')
        g1.switch()
    ​
    g1 = greenlet.greenlet(task1)
    g2 = greenlet.greenlet(task2)
    g1.switch()
    greenlet

    6.协程

    什么是协程?

    协程是单线程下的并发,又称微线程,纤程

    协程可以理解为提高线程工作效率,本质是单线程并发

    协程也称之为微线程(它比线程更轻量级 单线程下任务的切换 比操作系统切换线程要简单的多)

    为什么要有协程?

    因为CPython中无法实现并行执行任务,导致效率降低,需要一种方法将效率最大化

    协程的优点:

      1.协程的切换开销更小,属于程序级别的切换,操作系统完全能感知不到,因而更加轻量级

      2.单线程内就可以实现并发的效果,最大限度利用CPU

    协程的缺点:

      1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程

      2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

    怎么使用?

    Python中 使用Gevent模块来 实现协程 其能在多个任务间进行切换 而且能够自己检测IO

    from gevent import monkey
    monkey.patch_all()
    import gevent
    import time
    def task1():
        print("task1 run")
        time.sleep(10)
        print("task1 run")
    def task2():
        print("task2 run")
        print("task2 run")
    g1 = gevent.spawn(task1)
    g2 = gevent.spawn(task2)
    ​
    # g1.join()
    # g2.join()
    gevent.joinall([g1,g2])
    协程使用
    学习,学习,学习! 学习是为了更好的未来,不要让别人瞧不起你,加油!!!
  • 相关阅读:
    用 ArcMap 发布 ArcGIS Server FeatureServer Feature Access 服务 PostgreSQL 版本
    ArcMap 发布 ArcGIS Server OGC(WMSServer,MapServer)服务
    ArcScene 创建三维模型数据
    ArcMap 导入自定义样式Symbols
    ArcMap 导入 CGCS2000 线段数据
    ArcMap 导入 CGCS2000 点坐标数据
    ArcGis Server manager 忘记用户名和密码
    The view or its master was not found or no view engine supports the searched locations
    python小记(3)操作文件
    pytest(2) pytest与unittest的区别
  • 原文地址:https://www.cnblogs.com/yangyufeng/p/10220383.html
Copyright © 2011-2022 走看看