zoukankan      html  css  js  c++  java
  • 同步调用 异步调用+回调机制

    https://blog.csdn.net/freeking101/article/details/88119858

    1 同步调用 异步调用+回调机制
    提交任务的两种方式:
    什么是同步异步
    任务执行的三种状态:
    同步调用vs阻塞,两种不同的'等'的效果

    异步回调 ******
    什么是异步回调?
    为什么需要回调?(比如 烧水壶,水烧开后 水壶会发出响声)
    注意点:
    回调函数什么时候被执行?
    谁在执行回调函数?
    线程的异步回调

    同步:
    #所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回。
    按照这个定义,其实绝大多数函数都是同步调用。但是一般而言,
    我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
    #举例:
    #1. multiprocessing.Pool下的apply #发起同步调用后,就在原地等着任务结束,
    根本不考虑任务是在计算还是在io阻塞,总之就是一股脑地等任务结束
    #2. concurrent.futures.ProcessPoolExecutor().submit(func,).result()
    #3. concurrent.futures.ThreadPoolExecutor().submit(func,).result()

    异步:
    #异步的概念和同步相对。当一个异步功能调用发出后,调用者不能立刻得到结果。
    当该异步功能完成后,通过状态、通知或回调来通知调用者。如果异步功能用状态来通知,
    那么调用者就需要每隔一定时间检查一次,效率就很低
    (有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一 种很严重的错误)。
    如果是使用通知的方式,效率则很高,因为异步功能几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。
    #举例:
    #1. multiprocessing.Pool().apply_async() #发起异步调用后,并不会等待任务结束才返回,
    相反,会立即获取一个临时结果(并不是最终的结果,可能是封装好的一个对象)。
    #2. concurrent.futures.ProcessPoolExecutor(3).submit(func,)
    #3. concurrent.futures.ThreadPoolExecutor(3).submit(func,)

    阻塞:
    #阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作)。
    函数只有在得到结果之后才会将阻塞的线程激活。有人也许会把阻塞调用和同步调用等同起来,
    实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
    #举例:
    #1. 同步调用:apply一个累计1亿次的任务,该调用会一直等待,
    直到任务返回结果为止,但并未阻塞住(即便是被抢走cpu的执行权限,那也是处于就绪态);
    #2. 阻塞调用:当socket工作在阻塞模式的时候,
    如果没有数据的情况下调用recv函数,则当前线程就会被挂起,直到有数据为止。

    非阻塞:
    #非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程。

    小结:
    #1. 同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,
    一直等到函数(任务)完成,而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,
    不会等函数返回,而是继续往下执行当,函数返回的时候通过状态、通知、事件等方式通知进程任务完成。
    #2. 阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程


    2.线程队列 ***
    队列
    堆栈
    优先级队列

    3、单线程下实现并发(***)
    什么是协程
    并发
    并发实现的本质=切换+保存状态(两类切换)
    高性能分析:
    为什么需要协程
    如何实现协程(三种)
    协程的应用场景:
    总结点:




    ====================================
    1 同步调用 异步调用+回调机制
    提交任务的两种方式:
    同步调用 :提交任务必须等待任务完成,才能执行下一行
    异步调用 :提交任务不需要等待任务完成,立即执行下一行

    线程任务执行的三种状态:
    阻塞
    阻塞 遇到了IO操作 失去了CPU的执行权
    非阻塞:
    就绪
    运行

    同步调用vs阻塞,两种不同的'等'的效果
    同步调用的等 比如经过上千亿次计算,运行时间过长导致,被操作系统拿走执行权限,处于就绪态,非阻塞
    阻塞的等 比如经过IO操作,sleep了100秒,这是阻塞

    异步回调 ******
    什么是异步回调?
    发起了一个异步任务 子线程或子进程任务完成后 调用一个函数来通知任务发起方
    为什么需要回调?(比如 烧水壶,水烧开后 水壶会发出响声)
    由于任务是异步执行 任务发起方不知道啊什么时候完成
    所以使用回调的方式告诉发起方任务执行结果 其他方式也可以将数据交还给主进程
    1.shutdown 主进程会等到所有任务完成 # 类似于join的功能pool.shutdown(wait=True)
    2.result函数 会阻塞直到任务完成
    都会阻塞 导致效率降低 所以使用回调
    注意点:
    回调函数什么时候被执行? 子进程任务完成时
    谁在执行回调函数? 主进程
    线程的异步回调:
    使用方式都相同 唯一的不同是执行回调函数 是子线程在执行
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor

    pool = ThreadPoolExecutor()
    def get_data_task(url):
    return text
    def parser_data(f):
    print(f.result())
    if __name__ == '__main__':
    f = pool.submit(get_data_task,url) #get_data_task生产数据
    f.add_done_callback(parser_data) #parser_data处理数据

    2.线程队列 *** 见38复习
    队列
    堆栈
    优先级队列

    3、单线程下实现并发(***)
    什么是协程:
    单线程下实现并发,在应用程序级别实现多个任务之间切换+保存状态
    并发:看起来是同时运行
    并发实现的本质=切换+保存状态
    切换:
    1、遇到IO阻塞会切换(可以提升运行效率)
    2、占用cpu时间过长或者有一个优先级更高的任务抢走了cpu
    优点:
    1.协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因为更加轻量级
    2.单线程内就可以实现并发的效果,最大限度地利用cpu
    缺点:
    1.协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程下开启协程
    2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

    高性能分析:
    单纯地切换,或者说么有遇到io操作也切换,反而会降低效率
    检测单线程下的IO行为,实现遇到IO立即切换到其他任务执行

    为什么用协程? 多线程实现并发 有什么问题?
    TCP程序中 处理客户端的连接 需要子线程 但是子线程依然会阻塞
    一旦阻塞 CPU切走 但是无法保证是否切到当前程序
    提高效率的解决方案 是想办法尽可能多的占用CPU
    当程序遇到阻塞时 切换到别的任务 注意使用程序内切换


    协程的实现
    1 yield 把函数做成了生成器 生成器会自动保存状态
    2 greenlet 帮我们封装yield 可以实现任务切换
    创建对象时 制定任务就是函数 在函数中手动来switch切换任务 不能检测到IO行为
    3 gevent 封装了grennlet 既能够切换执行 也能检测IO

    使用gevent需要配合monkey补丁 monkey补丁内部将原本阻塞的模块 替换为了非阻塞的
    monkey必须放在导入(需要检测IO的模块)模块之前
    monkey.patch_all()
    gevent核心函数spawn(函数名)
    join让主线程等待所有任务执行完成才结束

    协程的应用场景:
    (没有IO绝对不使用协程) TCP 多客户端实现方式
    1.来一个客户端就来一个进程 资源消耗较大
    2.来一个客户端就来一个线程 也不能无限开
    3.用进程池 或 线程池 还是一个线程或进程只能维护一个连接
    4.协程 一个线程就可以处理多个客户端 遇到io就切到另一个

    总结协程特点:
    必须在只有一个单线程里实现并发,(将io阻塞时间用于执行计算 可以提高效率 原理:一直使用CPU直到超时)
    修改共享数据不需加锁
    用户程序里自己保存多个控制流的上下文栈
    附加:一个协程遇到IO操作自动切换到其它协程
    (如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))


    from gevent import monkey;monkey.patch_all()
    import gevent

    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])
  • 相关阅读:
    TWaver3D直线、曲线、曲面的绘制
    react开发教程(六)React与DOM
    java Web开发实现手机拍照上传到服务器
    手把手教你从零写一个简单的 VUE--模板篇
    file_get_contents函数获取不到数据的一种情况
    Windows系统下批处理快速创建WIFI
    操作系统题库之简答题部分(个人整理,附带答案)
    回溯到底是个什么东西(含有大量经典例题加详细分析)
    到底什么是dp思想(内含大量经典例题,附带详细解析)
    一次性弄懂到底什么叫做分治思想(含有大量经典例题,附带详细解析)
  • 原文地址:https://www.cnblogs.com/du-jun/p/9963301.html
Copyright © 2011-2022 走看看