zoukankan      html  css  js  c++  java
  • 异步同步、阻塞非阻塞、异步回调、线程队列和协程

      今天学习了异步同步、阻塞非阻塞、异步回调、线程队列和协程

      一、异步同步和阻塞非阻塞

      线程的三种状态:

        1、就绪

        2、运行

        3、阻塞

      阻塞:遇到了IO操作  代码卡住  无法执行下一行  CPU会切换到其他任务

      非阻塞: 与阻塞相反 代码正在执行(运行状态) 或处于就绪状态

      阻塞和非阻塞描述的是运行的状态

      同步:提交任务必须等待任务完成,才能执行下一行

      异步:提交任务不需要等待任务完成,立即执行下一行

      指的是一种提交任务的方式

      二、异步回调

        为什么回调:子进程帮助主进程完成任务 处理任务的结果应该交还给主进程
        其他方式也可以将数据交还给主进程

          1、shutdown  主进程会等到所有任务完成

          2、result函数  会阻塞直到任务完成

        注意:

          回调函数什么时候被执行?子进程完成时

          谁在执行回调函数?主进程

        线程的异步回调

          使用方式都相同  唯一的不同是执行回调函数 是子线程在执行

    #进程利用回调完成生产者消费者
    from
    concurrent.futures import ProcessPoolExecutor import os pool = ProcessPoolExecutor() #爬虫 从网络某个地址获取一个HTML文件 import requests #该模块用于网络请求 #生产数据 def get_data_task(url): print(os.getpid(),'正在生产数据!') response = requests.get(url) text = response.content.decode('utf-8') return text #处理数据 def parser_data(f): print(os.getpid(),'处理数据') print('正在解析:长度%s'%len(f.result())) urls = [ 'http://www.baidu.com', 'http://www.baidu.com', 'http://www.baidu.com', 'http://www.baidu.com' ] if __name__ == '__main__': for url in urls: f = pool.submit(get_data_task,url) f.add_done_callback(parser_data) #回调函数是主进程在执行 #因为子进程是负责获取数据的 然而数据怎么处理 子进程并不知道 应该把数据还给主进程 print('over')
    #线程利用回调完成生产者消费者
    from concurrent.futures import ThreadPoolExecutor
    from threading import current_thread
    
    pool = ThreadPoolExecutor
    #爬虫  从网络某个地址获取一个HTML文件
    import requests    #该模块用于网络(HTTP)请求
    #生产数据
    def get_data_task(url):
        print(current_thread(),'正在生产数据!')
        response = requests.get(url)
        text = response.content.decode('utf-8')
        return text
    
    #处理数据
    def parser_data(f):
        print(current_thread(),'处理数据')
        print('正在解析:长度%s'%len(f.result()))
    
    urls = [
    'http://www.baidu.com',
    'http://www,baidu.com',
    'http://www.baidu.com',
    'http://www.baidu.com'
    ]
    
    if __name__ =='__main__':
        for url in urls:
            f = pool.submit(get_data_task,url)
            f.add_done_callback(parser_data)    #因为是子线程在执行回调函数 所以没有主次之分 任何子线程都可以对函数进行回调
        print('over')

      三、线程队列

      

    import queue
    #普通队列 先进先出
    q = queue.Queue()
    q.put('a')
    q.put('b')
    print(q.get())
    print(q.get())
    
    #堆栈队列  先进后出  函数调用就是进栈  函数结束就出栈  递归造成栈溢出
    q2 = queue.LifoQueue()
    q2.put('a')
    q2.put('b')
    print('q2.get()')
    
    #优先级队列
    q3 = queue.PriorityQueue()    #数值越小优先级越高  优先级相同时  比较大小 小的先取
    q3.put((-100,'c'))
    q3.put((1,'a'))
    q3.put((100,b))
    print(q3.get())

      四、协程

        协程的目的是在单线程下实现并发

        单线程下实现并发 将io阻塞时间用于执行计算 可以提高效率 原理:一直使用CPU直到超时

        怎么实现单线程并发?

        并发  指的是  看起来像是同时运行 实际是在任务间来回切换 同时需要保存执行的状态

        任务是一堆代码 可以用函数装起来

        1.如何让两个函数切换执行

          yield可以保存函数的执行状态

          通过生成器可以实现伪并发

          并发不一定提升效率  反而会降低效率 当任务全是计算时

        2.如何知道发生了io?从而切换执行

          目前咱们实现不了。。

        第三方模块 greenlet 可以实现并发 但是不能检测io

        第三方模块 gevent 封装greenlet 可以实现单线程并发 并且能够检测io操作 自动切换

        

    #用yield实现两个函数切换执行
    import time
    def task():
        while True:
            print('task1')
            time.sleep(4)
            yield 1
    
    def task2():
        g = task()
        while True:
            try:
                print('task2')
                next(g)
            except Exception:
                print('任务完成')
                break
    task2()
    #使用greenlet模块实现并发
    import greenlet
    import time
    def task1():
        print('task1 1')
        time.sleep(2)
        g2.switch()
        print('task1 2')
        g2.swith()
    
    def task2():
        print('task2 1')
        g1.switch()
        print('task2 2')
    
    g1 = greenlet.greenlet(task1)
    g2 = greenlet.greenlte(task2)
    g1.switch()
    #1.实例化greenlet得到一个对象 传入要执行的任务
    #2.先让某个任务执行起来 使用对象调用switch
    #3.在任务的执行过程中 手动调用switch来切换
    #使用gevent模块实现单线程的并发
    from gevent import monkey
    monkey.patch_all()
    import gevent
    import time
    def eat():
        print('eat food 1')
        time.sleep(2)
        print('eat food 2')
    
    def play():
        print('play 1')
        time.sleep(1)
        print('play 2')
    
    g1 = gevent.spawn(eat)
    g2 = gevent.spawn(play)
    gevent.joinall([g1,g2])
    print('')
    
    #1.spawn函数传入你的任务
    #2.调用join 去开启任务
    #3.检测io操作需要打mokey补丁  就是一个函数  在程序最开始的地方调用它
  • 相关阅读:
    谷歌三架马车之 The Google File System 中文版
    数据集市 Data Mart
    VMware虚拟机ubuntu下安装VMware Tools步骤
    NOIP普及组 海港 题解
    HXD的DS
    离散化
    哈希表
    状态压缩DP 初探
    《信息学奥赛一本通》大盗阿福 题解
    NOIP 加工零件 题解
  • 原文地址:https://www.cnblogs.com/xiaocaiyang/p/9954077.html
Copyright © 2011-2022 走看看