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])
    协程使用
    学习,学习,学习! 学习是为了更好的未来,不要让别人瞧不起你,加油!!!
  • 相关阅读:
    NFS
    Linux ISO镜像挂载
    Python3.6 提示 ModuleNotFoundError: No module named '_ssl' 模块问题
    mysql 5.7 ERROR 1054(42S22) Unknown column 'password' in ‘field list’ 报错
    Redis + keepalived 高可用行配置检测脚本
    Linux 文件大小查找排序
    查看 Centos 7 的MAC 地址
    Discuz 论坛 (LAMP环境)
    SVN
    systemctl
  • 原文地址:https://www.cnblogs.com/yangyufeng/p/10220383.html
Copyright © 2011-2022 走看看