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])
  • 相关阅读:
    trackr: An AngularJS app with a Java 8 backend – Part III
    trackr: An AngularJS app with a Java 8 backend – Part II
    21. Wireless tools (无线工具 5个)
    20. Web proxies (网页代理 4个)
    19. Rootkit detectors (隐形工具包检测器 5个)
    18. Fuzzers (模糊测试器 4个)
    16. Antimalware (反病毒 3个)
    17. Debuggers (调试器 5个)
    15. Password auditing (密码审核 12个)
    14. Encryption tools (加密工具 8个)
  • 原文地址:https://www.cnblogs.com/du-jun/p/9963301.html
Copyright © 2011-2022 走看看