精华内容
下载资源
问答
  • 同步编程和异步编程

    千次阅读 2018-04-27 13:11:04
    异步编程: 为了避免资源管理等复杂性的问题, javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是为浏览器设计的GUI编程语言,GUI编程的特性之一是保证UI线程...

    同步编程:

    传统的同步编程是一种请求响应模型,调用一个方法,等待其响应返回.

    异步编程:

    为了避免资源管理等复杂性的问题,

    javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom.

    javascript 设计之初是为浏览器设计的GUI编程语言,GUI编程的特性之一是保证UI线程一定不能阻塞,否则体验不佳,甚至界面卡死。

    一般安卓开发,会有一个界面线程,一个后台线程,保证界面的流畅。

    由于javascript是单线程,所以采用异步非阻塞的编程模式,javascript的绝大多数api都是异步api.

    本文是本人的一个总结:从Brendan Eich刚设计的初版javascript到现在的ES6,一步步总结javascript异步编程历史。

    什么是异步编程

    那么什么是异步编程,异步编程简单来说就是:执行一个指令不会马上返回结果而执行下一个任务,而是等到特定的事件触发后,才能得到结果。

    这里写图片描述
    异步编程时就需要指定异步任务完成后需要执行的指令,总的来说有以下几种“指定异步指令”的方式:

    属性
    回调
    Promise
    Generator
    await,async
    下面会一步一步展现各种方式。

    指定异步指令参照地址
    对异步编程的理解

    展开全文
  • 同步编程和异步编程 看到这里的图。才突然有些理解。学概念的时候,同步是相对线程来说的,相对于线程。所有的任务(我们写的代码)是一同在最开始的时候就放进线程的。顺序都是定好的。自然会有阻塞。 而异步是,谁...

    算是历史遗留问题。不太理解到底什么是同步。 汉语意思,平常生活中感觉同步才是一起同时做一件事。可是概念学的时候,同步是挨个执行,异步才是一起执行。

    找了好久,看到这个文章。

    同步编程和异步编程

    看到这里的图。才突然有些理解。学概念的时候,同步是相对线程来说的,相对于线程。所有的任务(我们写的代码)是一同在最开始的时候就放进线程的。顺序都是定好的。自然会有阻塞。

    而异步是,谁要执行了。直接放到线程当前去执行。线程执行的时候则是,来回切换任务。让人感觉是没有阻塞。

    理解起来虽然有点牵强。但是,目前自己只能这么理解。不然。按照生活常识概念理解。太难了。那帮人讲个概念总是举生活中的吃饭,洗衣服的例子。本概念不同,还要跨概念的去理解别的,然后让学人自己转换。。。zl

    展开全文
  • 1 同步和异步 函数或方法掉调用的时候,被调用者是否能得到最终结果来判断同步和异步 直接得到最终结果的,就是同步调用不直接得到最终结果的,就是异步调用 同步就是我让你打饭,你不打好我就不走开,直到你打饭给...

    一 简介

    1 同步和异步

    函数或方法掉调用的时候,被调用者是否能得到最终结果来判断同步和异步
    直接得到最终结果的,就是同步调用
    不直接得到最终结果的,就是异步调用

    同步就是我让你打饭,你不打好我就不走开,直到你打饭给了我
    异步就是我让你打饭,你大着,我不等你,但是我会盯着你,你打完我会过来拿走,异步并不能保证多长时间将饭打完。

    异步给的是临死结果,目前是拿不到的
    同步只看结果是不是最终结果进行判断

    2 阻塞,非阻塞

    函数或方法调用的时候,是否立即返回
    立即返回就是非阻塞调用
    不立即返回就是阻塞调用

    3 区别

    同步,异步,阻塞,非阻塞 不相关
    同步异步强调的是结果
    阻塞,非阻塞强调的是时间,是否等待

    同步和异步的区别在于:调用者是否得到可想要的结果

    同步就是一直要执行到返回结果

    异步就是直接返回了,但是不是最终结果,调用者不能通过这种调用方式得到结果,还是需要通过被调用者,使用其他方式通知调用者,来取回最终的结果

    同步阻塞:我啥事也不干,就等你打饭给我,打饭是结果,而且我啥事也不敢就一直等,同步加阻塞。

    同步非阻塞:我等着你打饭给我,但我可以完手机,看电视,打饭是结果,但我不一直等

    异步阻塞: 我要打饭,你说等号,并没有给我返回饭,我啥事也不干,就等着饭好了叫我,叫号。

    异步非阻塞:我要打饭,你说等号,并没有返回饭,我在旁边看电视,玩手机,反打好了叫我。

    4 同步IO,异步IO,IO 多路复用

    1 IO 两个阶段

    1 数据准备阶段
    2 内核空间复制会用户进程缓冲区阶段

    2 发生IO的时候

    1 内核从输入设备读写数据
    2 进程从内核复制数据
    系统调用read 函数
    第一个IO阻塞的函数是input函数,是一个同步阻塞模型,网络也是一个IO,标准输入,标准输出等也IO

    5 零拷贝

    1 零拷贝概念

    CPU 不执行拷贝数据从一个存储区域到另一个存储区域的任务,这通常用于通过网络传输一个文件时用于减少CPU周期和内存带宽。

    操作系统某些组件(例如驱动程序、文件系统和网络协议栈)若采用零复制技术,则能极大地增强了特定应用程序的性能,并更有效地利用系统资源。通过使CPU得以完成其他而非将机器中的数据复制到另一处的任务,性能也得到了增强。另外,零复制操作减少了在用户空间与内核空间之间切换模式的次数。

    零复制协议对于网络链路容量接近或超过CPU处理能力的高速网络尤为重要。在这种网络下,CPU几乎将所有时间都花在复制要传送的数据上,因此将成为使通信速率低于链路容量的瓶颈。

    2 零拷贝带来的好处

    1 减少甚至完全避免不必要的CPU拷贝,从而让CPU 解脱出来去执行其他任务
    2 减少内存带宽占用
    3 通常零拷贝技术还能减少用户空间和内核空间之间的上下文切换

    3 Linux 系统的"用户空间"和"内核空间"

    从Linux系统来看,除了引导系统的BIN区,整个内存空间主要被分成两部分:

    1 内核空间(kernel space ) : 主要提供给程序调度,内存分配,连接硬件资源等程序逻辑空间

    2 用户空间 (user space): 提供给各个进程的主要空间,用户空间不具备访问内核空间资源的权限,因此如果应用程序需要使用到内核空间的资源,则需要通过系统调度来完成,从用户空间切换到内核空间,然后在完成操作后再从内核空间切换到用户空间

    4 Linux 中的零拷贝技术的实现方向

    1 直接I/O: 对于这种传输方式来说,应用程序可以直接访问硬件存储,操作系统内核只是辅助数据传输,这种方式依旧存在用户空间和内核空间的上下文切换,但硬件上的数据不会拷贝到内核空间,而是直接拷贝到可用户空间,因此直接IO不存在内核空间缓冲区和用户空间缓冲区之间的数据拷贝


    2 在数据传输过程中,避免数据在用户空间缓冲区和内核空间缓冲区之间的CPU拷贝,以及数据在系统内核空间的CPU拷贝,


    3 copy-on-write(写时复制技术):在某些情况下,Linux操作系统的内核缓冲区可能被多个应用程序共享,操作系统有可能会将用户空间缓冲区地址映射考内核空间缓冲区,当应用程序需要对共享的数据进行修改时,才需要真正的拷贝数据到应用程序的用户空间缓冲区中,并且对自己的用户空间的缓冲区的数据进行修改不会影响到其他共享数据的应用程序,所以,如果应用程序不需要对数据进行任何修改,就不会存在数据从系统内核空间缓冲区拷贝到用户空间缓冲区的操作。

    对于零拷贝技术是否实现主要依赖于操作系统底层是否提供相应的支持。

    5 传统I/O 操作

    1 发起read系统调用: 导致用户空间到内核空间的上下文切换(第一次上下文切换),通过DMA引擎将文件中的数据从磁盘上读取到内核空间缓冲区(第一次拷贝:hand drive ----> kernel buffer)


    2 将内核空间缓冲区的数据拷贝到用户空间缓冲区中(第二次拷贝: kernel buffer ---> user buffer),然后read系统调用返回,而系统调用的返回又会导致一次内核空间到用户空间的上下文切换(第二次上下文切换)


    3 发出write系统调用: 导致用户空间到内核空间的上下文切换(第三次上下文切换),将用户空间缓冲区的数据拷贝到内核空间中于socket相关的缓冲区中,(及第二步从内核空间缓冲区拷贝的数据原封不动的再次拷贝到内核空间的socket缓冲区中)( 第三次拷贝: user buffer--> socket buffer)


    4 write 系统调用返回,导致内核空间到用户空间的再次上下文切换(第四次上下文切换),通过DMA引擎将内核缓冲区中的数据传递到协议引擎(第四次拷贝:socket buffer -> protocol engine ),这次拷贝时独立的异步的过程。


    事实上调用的返回并不保证数据被传输,甚至不保证数据传输的开始,只是意味着将我么要发送的数据放入到了一个待发送的队列中,除非实现了优先环或者队列,否则会是先进先出的方式发送数据的。

    总的来说,传统的I/O操作进行了4次用户空间与内核空间的上下文切换,以及4次数据拷贝。其中4次数据拷贝中包括了2次DMA拷贝和2次CPU拷贝。

    传统模式为何将数据从磁盘读取到内核空间而不是直接读取到用户空间缓冲区,其原因是为了减少IO操作以提高性能,因为OS会根据局部性原理一次read() 系统调用的时候预读取更多的文件数据到内核空间缓冲区中,这样当下一次read()系统调用的时候发现要读取的数据已经存在于内核空间缓冲区的时候只需要直接拷贝数据到用户空间缓冲区即可,无需再进行一次低效的磁盘IO操作。

    Bufferedinputstream 作用是会根据情况自动为我们预读取更多的数据到他自己维护的一个内部字节数据缓冲区,这样能减少系统调用次数来提高性能。

    总的来说,内核缓冲区的一大作用是为了减少磁盘IO操做,Bufferedinputstream 则是减少"系统调用"

    6 DMA

    DMA(direct memory access) --- 直接内存访问,DMA 是允许外设组件将IO数据直接传送到主存储器并并且传输不需要CPU参与,以此解放CPU去做其他的事情。
    而用户空间与内核空间之间的数据传输并没有类似DMA这种可以不需要CPU参与的传输工具,因此用户空间与内核空间之间的数据传输是需要CPU全程参与的。所有也就有了通过零拷贝技术来减少和避免不必要的CPU数据拷贝过程。

    7 通过sendfile 实现零拷贝IO

    1 发起sendfile系统调用,导致用户空间到内核空间的上下文切换(第一次上下文切换),通过DMA引擎将磁盘文件中的内容拷贝到内核空间缓冲区中(第一次拷贝: hard drive --> kernel buffer)然后再将数据从内核空间拷贝到socket相关的缓冲区中,(第二次拷贝,kernel ---buffer --> socket buffer)


    2 sendfile 系统调用返回,导致内核空间到用户空间的上下文切换(第二次上下文切换)。通过DMA 引擎将内核空间的socket缓冲区的数据传递到协议引擎(第三次拷贝:socket buffer-> protocol engine )


    总的来说,通过sendfile实现的零拷贝I/O只使用了2次用户空间与内核空间的上下文切换,以及3次数据的拷贝。其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝。

    Q:但通过是这里还是存在着一次CPU拷贝操作,即,kernel buffer ——> socket buffer。是否有办法将该拷贝操作也取消掉了?
    A:有的。但这需要底层操作系统的支持。从Linux 2.4版本开始,操作系统底层提供了scatter/gather这种DMA的方式来从内核空间缓冲区中将数据直接读取到协议引擎中,而无需将内核空间缓冲区中的数据再拷贝一份到内核空间socket相关联的缓冲区中。

    8 带有DMA 收集拷贝功能的sendfile 实现的IO

    从Linux 2.4 开始,操做系统底层提供了带有scatter/gather 的DMA来从内核空间缓冲区中将数据读取到协议引擎中,这样以来待传输的数据可以分散再存储的不同位置,而不需要再连续存储中存放,那么从文件中读出的数据就根本不需要被拷贝到socket缓冲区中去,只是需要将缓冲区描述符添加到socket缓冲区中去,DMA收集操作会根据缓冲区描述符中的信息将内核空间中的数据直接拷贝到协议引擎中


    1 发出sendfile 系统调用,导致用户空间到内核空间的上下文切换,通过DMA 引擎将磁盘文件内容拷贝到内核空间缓冲区中(第一次拷贝: hard drive -> kernel buffer)


    2 没有数据拷贝到socket缓冲区,取而代之的是只有向相应的描述信息被拷贝到相应的socket缓冲区中,该描述信息包含了两个方面: 1 kernel buffer 的内存地址 2 kernel buffer 的偏移量。


    3 sendfile 系统调用返回,导致内核空间到用户空间的上下文切换(第二次上下文切换),DMA gather copy 根据 socket缓冲区中描述符提供的位置和偏移量信息直接将内核空间的数据拷贝到协议引擎上(kernel buffer --> protocol engine),这样就避免了最后依次CPU数据拷贝


    总的来说,带有DMA收集拷贝功能的sendfile实现的I/O只使用了2次用户空间与内核空间的上下文切换,以及2次数据的拷贝,而且这2次的数据拷贝都是非CPU拷贝。这样一来我们就实现了最理想的零拷贝I/O传输了,不需要任何一次的CPU拷贝,以及最少的上下文切换。

    在Linux 2.6.33 版本之前sendfile支持文件到套接字之间的传输,及in_fd 相当于一个支持mmap的文件,out_fd 必须是一个socket,但从Linux 2.6.33版本开始,out_fd 可以是任意类型文件描述符,所以从Linux 2.6.33 版本开始sendfile 可以支持文件到文件,文件到套接字之间的数据传输。

    9 传统I/O 和零拷贝及sendfile零拷贝I/O比较

    传统I/O通过两条系统指令read、write来完成数据的读取和传输操作,以至于产生了4次用户空间与内核空间的上下文切换的开销;而sendfile只使用了一条指令就完成了数据的读写操作,所以只产生了2次用户空间与内核空间的上下文切换。
    传统I/O产生了2次无用的CPU拷贝,即内核空间缓存中数据与用户空间缓冲区间数据的拷贝;而sendfile最多只产出了一次CPU拷贝,即内核空间内之间的数据拷贝,甚至在底层操作体系支持的情况下,sendfile可以实现零CPU拷贝的I/O。
    因传统I/O用户空间缓冲区中存有数据,因此应用程序能够对此数据进行修改等操作;而sendfile零拷贝消除了所有内核空间缓冲区与用户空间缓冲区之间的数据拷贝过程,因此sendfile零拷贝I/O的实现是完成在内核空间中完成的,这对于应用程序来说就无法对数据进行操作了。
    Q:对于上面的第三点,如果我们需要对数据进行操作该怎么办了?
    A:Linux提供了mmap零拷贝来实现我们的需求

    10 通过mmap 实现零拷贝I/O

    Mmap(内存映射)是一个比sendfile昂贵但优于传统IO的方式

    1 发出mmap系统调用,导致用户空间到内核空间的上下文切换(第一次上下文切换)。通过DMA引擎将磁盘文件中的内容拷贝到内核空间缓冲区中(第一次拷贝: hard drive ——> kernel buffer)。


    2 mmap 系统调用返回,导致内核空间到用户空间的上下文切换(第二次上下文切换),接着用户空间和内核空间共享这个缓冲区,而不需要将数据从内核空间拷贝到用户空间,因此用户空间和内核空间共享的缓冲区


    3 发出write 系统调用红,导致用户空间到内核空间第三次上下文切换,将数据从内核空间拷贝到内核空间的socket相关的缓冲区(第二次拷贝:kernel buffer ----> socket buffer )


    4 write 系统调用返回,导致内核空间到用户空间的上下文切换(第四次上下文切换),通过DMA 引擎将内核空间socket缓冲区的数据传递到协议引擎(第三次拷贝: socket buffer---> protocol engine)

    总的来说,通过mmap实现的零拷贝I/O进行了4次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝。

    6 同步IO

    1 同步阻塞IO

    python同步编程和异步编程

    在文件读取进入内核空间和从内核空间拷贝进入用户进程空间的过程中,没有任何的数据返回,客户端在一直等待状态。

    2 同步非阻塞

    python同步编程和异步编程

    进程调用read操作,如果IO没有准备好,立即返回ERROR,进程不阻塞,用户可以再次发起系统调用,如果内核已经准备好,就阻塞,然后复制数据到用户空间

    第一阶段数据没准备好,就先忙别的,等会再看看,检查数据是否准备好了的过程是非阻塞的

    第二阶段是阻塞的,及内核空间和用户空间之间复制数据是阻塞的,但是要等待饭盛好才是完事,这是同步的。

    3 IO 多路复用

    python同步编程和异步编程

    所谓的IO多路复用,就是同时监控多个IO,有一个准备好了,就不需要等待开始处理,提高了同时处理IO的能力

    select是所有平台都支持,poll是对select的升级

    epoll,Linux 系统内核2.5+ 开始支持,对select和epoll的增强,在监视的基础上,增加了回调机制,BSD,Mac的kqueue,还有windows的iocp

    如果既想访问网络,又想访问文件,则先将准备好的数据先处理,那个准备好了就处理那个

    能够提高同时处理IO的能力,谁先做玩我先处理谁

    上面的两种方式,效率太差了,等完一个完成后再等一个,太慢了。

    谁好了处理谁,不同的平台对IO多路复用的实现方式是不同的

    Select 和 poll 在Linux,Windows,和MAC中都支持

    一般来将select和poll 在同一个层次,epoll是Linux中存在的


    select原理

    1 将关注的IO操作告诉select函数并调用,进程阻塞,内核监视select关注的文件,描述符FD,被关注的任何一个FD对应的IO准备好了数据,select就返回,在使用read将数据复制到用用户进程。其select模式下的准备好的通知是没有针对性的,需要用户自己找到是否是自己的并进行处理。select做到的是时间重叠

    epoll增加了回调机制,那一路准备好了,我会告诉你,有一种是你不用管了,好了我直接替你调用。

    7 异步调用

    python同步编程和异步编程

    两个阶段
    等待数据准备和拷贝阶段
    立即返回数据,给一个号。到时候叫号,直接返回
    信号句柄,告诉你几号好了,(signal handler process datagram)
    有些时候是需要争抢的
    我可以不通知你,我也可以通知你后你再来
    理解数据层面的东西,就不要理解其他的socket层面的东西
    文件中实际就是两个缓冲队列,每个队列是一个。
    在异步模型中,操作系统通你的,你是在用户空间的,操作系统可以是在内核空间的,进程和线程等等的都是操作系统层面的东西。
    整个过程中进程都可以做其他的事,就算是通知了,也不一定要立即反应,这和你的设置有关
    Linux中的AIO 的系统调用,内核版本从2.6开始支持
    一般的IO是IO多路复用和异步复用

    二 python中的IO 多路复用

    1 简介

    IO 多路复用
    大多数操作系统都支持select和poll
    Linux 2.5+ 支持epoll
    BSD,Mac支持kqueue
    Windows 的 iocp
    python的select库

    实现了select,poll系统调用,这个基本上操作系统都支持,部分实现了epoll,底层的IO多路复用模块

    开发中的选择

    1 完全跨平台,select 和poll ,但其性能较差
    2 针对不同的操作系统自行选择支持技术,这样会提高IO处理能力

    selectors库
    3.4 版本后提供这个库,高级的IO复用库
    类层次结构

    BaseSelector
    +-- SelectSelector 实现select
    +-- PollSelector 实现poll
    +-- EpollSelector 实现epoll
    +-- DevpollSelector 实现devpoll
    +-- KqueueSelector 实现kqueue

    selectors.DefaultSelector返回当前平台最有效,性能最最高的实现
    但是由于没有实现windows的IOCP,所以只能退化为select。
    默认会自适应,其会选择最佳的方式,Linux 会直接选择 epoll ,通过此处,能拿到平台的最优方案。

    DefaultSelector 源码

    if 'KqueueSelector' in globals():
        DefaultSelector = KqueueSelector
    elif 'EpollSelector' in globals():
        DefaultSelector = EpollSelector
    elif 'DevpollSelector' in globals():
        DefaultSelector = DevpollSelector
    elif 'PollSelector' in globals():
        DefaultSelector = PollSelector
    else:
        DefaultSelector = SelectSelector

    2 基本方法

    abstractmethod register(fileobj,events,data=None)

    为selection注册一个文件独享,监视它的IO事件
    fileobj 被监视的文件对象,如socket对象
    events 事件,该文件对象必须等待的事件,read或write

    python同步编程和异步编程

    data 可选的与此文件对象相关的不透明数据,如可用来存储每个客户端的会话ID,可以是函数,类,实例,如果是函数,有点回调的意思,通知某个函数,某个实例,某个类,可以是类属性,等,都可以,None表示消息发生了,没人认领。

    3 基本实现socket 操作监控

    1 思路

    第一步 :需要实例化 ,选择一个最优的实现,将其实例化(选择不同平台实现的IO复用的最佳框架),python内部处理


    第二步:注册函数,将要监控对象,要监控事件和监控触发后对象写入register注册中

    1 注册: 对象,啥事件,调用的函数
    2 进行循环和监控select函数的返回,当监控的对象的事件满足时会立即返回,在events中可以拿到这些数据events中有我是谁,我是什么事件触发的(读和写),读的满足可以recv,key 是让我监控的东西,event是其什么事件触发的。将对象和事件拿到后做相应的处理。


    第三步:实时关注socket有读写操作,从而影响events的变化

    对socket来判断有没有读,若读了,则直接触发对应的机制进行处理。一旦有新的连接准备,则会将其消息发送给对应的函数进行处理相关的操作。被调用的函数是有要求的,其需要传送mask的,data 就是未来要调用的函数,建立了事件和未来参数之间建立的关系。

    Accept 本身就是一个read事件
    Selector 会调用自己的select函数进行监视,这个函数是阻塞的,当数据已经在内核缓冲区准备好了,你就可以读取了,这些事给select进行处理

    在注册的时候,后面加了data,后面直接使用,直接调用,不用管其他,data和每一个观察者直接对应起来的。

    只要有一个满足要求,直接返回

    读事件指的是in操作,及就是当有连接的时候

    当通知成功后,其函数内部是不会阻塞了,等待通知,通知成功后就不会阻塞了。此处的data相当于直接带着窗口号,直接进行处理,而不需要一个一个的遍历

    当一个满足了,就不会阻塞了。events: 两个IO都满足,等待几路,几路的IO都在此处,如果满足,则直接向下打印events,其中key是注册的唯一的东西,socket 也可以,但是可以定义socket的读和写,一般都是合着的


    第四步:调用对应事件的对象,并执行相关操作

    然后将events拿出来解构,key本身是一个多元祖,key上保存着注册塞进去的data,key是存储了4个信息的元祖,此处的data称为回调函数,加上() 称为调用

    2 代码实现

    python同步编程和异步编程
    python同步编程和异步编程

    代码下载目录
    IO 多路复用初始代码

    https://pan.baidu.com/s/18B5OL89Z4YSxEmX4gNkgDA

    3 基本参数讲解

    1 events参数:

    2019-09-01 09:37:46 Thread-1 events: [(SelectorKey(fileobj=<socket.socket fd=4, family=AddressFamily.AF_INET, type=2049, proto=0, laddr=('192.168.1.200', 9999)>, fd=4, events=1, data=<function accept at 0x7f50feb61d90>), 1)]

    events中包含了两组
    第一组 :
    fileobj 及套接字返回的相关参数,和之前的socket中的accpet中的conn 相似,

    fd 及文件描述符

    events 及事件类型,python同步编程和异步编程两种

    data 及注册调用的函数,上述的有accept 和recv 函数


    第二组:
    1 events 的状态,及mask

    2 select.get_map() 参数:

    1 select.get_map().items() 中的key
    2019-09-01 09:43:52 MainThread key:SelectorKey(fileobj=<socket.socket fd=4, family=AddressFamily.AF_INET, type=2049, proto=0, laddr=('192.168.1.200', 9999)>, fd=4, events=1, data=<function accept at 0x7fcf5a50ad90>)

    此处的key和上面的列表中的二元祖中的前一个完全相同


    2 select.get_map().items() 中的fobj
    2019-09-01 09:43:52 MainThread fobj: 4
    其是其中的文件描述符

    4 总结:

    IO 多路复用就是一个线程来处理所有的IO
    在单线程中进行处理IO多路复用
    多线程中的IO阻塞时浪费CPU资源,其是等待状态,等待状态虽然不占用CPU资源,但线程本身的状态需要维持,还是会占用一定的资源

    4 改进版本的socket 监控

    1 描述

    send 是写操作,有可能阻塞,也可以监听
    recv所在的注册函数,要监听python同步编程和异步编程读与写事件,回调的时候,需要mask 来判断究竟是读触发了还是写触发了,所以,需要修改方法声明,增加mask
    写操作当发送群聊时,其每个链接是独立的,需要queue队列保存相关的数据,并进行接受和发送操作

    2 代码实现

    python同步编程和异步编程
    python同步编程和异步编程

    python同步编程和异步编程

    IO 多路复用最终代码

    https://pan.baidu.com/s/1y-3j607_5DxBpa4wZNxCEQ

    三 异步编程

    1 asyncio 简介

    3.4 版本加入标准库
    asyncio 底层是基于selectors实现的,看似库,其实就是一个框架,包括异步IO,事件循环,协程,任务等


    并行和串行的区分:
    两个事件的因果关系:
    若有因果关系,则可以使用串行
    若无因果关系,则可以使用并行,及多线程来处理

    2 相关参数及详解

    参数 含义
    asyncio.get_event_loop() 返回一个事件循环对象,是asyncio.BaseEventLoop的实例
    AbstractEventLoop.stop() 停止运行事件循环
    AbstractEventLoop.run_forever() 一直运行,直到stop()
    AbstractEventLoop.run_until_complete(future) 运行直到Future对象运行完成
    AbstractEventLoop.close() 关闭事件循环
    AbstractEventLoop.is_running() 返回事件循环是否运行
    AbstractEventLoop.close() 关闭事件

    3 协程

    1 基本实例

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   threading
    def  a():
        for i in range(3):
            print (i)
    
    def  b():
        for  i  in "abc":
            print (i)
    
    a()
    b()
    

    python同步编程和异步编程

    此处的默认执行顺序是a()到b()的顺序执行,若要使其交叉执行,则需要使用yield 来实现

    实现方式如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   threading
    import multiprocessing
    def  a():
        for i in range(3):
            print (i)
            yield
    
    def  b():
        for  i  in "abc":
            print (i)
            yield
    
    a=a()
    b=b()
    for  i in range(3):
        next(a)
        next(b)

    python同步编程和异步编程

    上述实例中通过生成器完成了调度,让两个函数都几乎同时执行,这样的调度不是操作系统进行的。而是用户自己设计完成的


    这个程序编写要素:
    1 需要使用yield来让出控制权
    2 需要循环帮助执行

    2 协程简介

    协程不是进程,也不是线程,它是用户空间调度的完成并发处理的方式。
    进程,线程由操作系统完成调度,而协程是线程内完成调度的,不需要更多的线程,自然也没有多线程切换的开销
    协程是非抢占式调度,只有一个协程主动让出控制权,另一个协程才会被调度。
    协程也不需要使用锁机制,因为其是在同一个线程中执行的
    多CPU下,可以使用多进程和协程配合,既能进程并发,也能发挥出协程在单线程中的优势。
    python中的协程是基于生成器的。

    3 协程基本书写

    3.4 引入的asyncio,使用装饰器

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   threading
    import multiprocessing
    import asyncio
    @asyncio.coroutine
    def  a():
        for i in range(3):
            print (i)
            yield
    loop=asyncio.get_event_loop()
    loop.run_until_complete(a())
    loop.close()

    结果如下

    python同步编程和异步编程

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   threading
    import multiprocessing
    import asyncio
    @asyncio.coroutine
    def  a():
        for i in range(3):
            print (i)
            yield
    @asyncio.coroutine
    def b():
        for  i in "abc":
            print(i)
            yield
    
    loop=asyncio.get_event_loop()
    task=[a(),b()]
    loop.run_until_complete(asyncio.wait(task))
    loop.close()

    结果如下

    python同步编程和异步编程

    3.5 及其以后版本的书写方式:

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   threading
    import multiprocessing
    import asyncio
    async  def   a():
        for i in range(3):
            print (i)
            # await  asyncio.sleep(0.0001)
    async  def   b(): #使用此方式后,不能再次使用wait了
        for  i in "abc":
            print(i)
            # await  asyncio.sleep(0.0001)
    
    print (asyncio.iscoroutinefunction(a)) # 此处判断是否是函数,和调用无关
    
    a=a()
    print (asyncio.iscoroutine(a))  # 此处是判断对象,是调用后的结果
    loop=asyncio.get_event_loop()
    task=[a,b()]
    loop.run_until_complete(asyncio.wait(task))
    loop.close()
    

    结果如下

    python同步编程和异步编程

    async def 用来定义协程函数,iscoroutinefunction()返回True,协程函数中可以不包含await,async关键字,但是不能使用yield关键字
    如果生成器函数调用返回生成器对象一样,协程函数调用也会返回一个对象成为协程对象,iscoroutine()返回为True

    4 TCP echo server 实现

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   threading
    import multiprocessing
    import asyncio
    import  socket
    ip='192.168.1.200'
    port=9999
    async def  handler(conn,send):
        while True:
            data=await   conn.read(1024) # 接受客户端的数据,相当于recv,wait 就是IO等待,此处会等待
            print (conn,send)
            client_addr=send.get_extra_info('peername')  # 获取客户端信息
            msg="{} {}".format(data.decode(),client_addr).encode()  #封装消息
            send.write(msg)  # 传输到客户端
            await    send.drain()  # 此处相当于makefile中的flush ,此处也会IO等待
    
    loop=asyncio.get_event_loop() #实例化一个循环事件
    crt=asyncio.start_server(handler,ip,port,loop=loop) #使用异步方式启动函数,最后一个参数是应该用谁来循环处理
    server=loop.run_until_complete(crt) # 此处是直到此方法完成后终止
    
    print (server)
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.close()
        loop.close()

    5 扩展aiohttp 库

    异步的http 库,使用协程实现的
    需要安装第三方模块 aiohttp

    pip  install  aiohttp 

    http server 基础实现

    #!/usr/bin/poython3.6
    #conding:utf-8
    
    from  aiohttp  import web
    async def  indexhandle(request:web.Request):  # 处理客户端请求函数
        print("web",web.Request)
        return web.Request(text=request.path,status=201) #返回文本和状态码
    
    async def  handle(request:web.Request):
        print (request.match_info)
        print (request.query_string)
        return web.Response(text=request.match_info.get('id','0000'),status=200)  # 此处是返回给客户端的数据,后面的0000是默认
    
    app=web.Application()
    
    #路由选路,
    app.router.add_get('/',indexhandle) # http://192.168.1.200:80/
    app.router.add_get('/{id}',handle)  # http://192.168.1.200:80/12345
    
    web.run_app(app,host='0.0.0.0',port=80)  #监听IP和端口并运行

    客户端实现

    #!/usr/bin/poython3.6
    #conding:utf-8
    import asyncio
    from aiohttp import ClientSession
    
    async  def  get_html(url:str):
        async with   ClientSession()  as  session:  # 获取session,要和服务端通信,必须先获取session,之后才能进行相关的操作 ,此处使用with是打开关闭会话,保证会话能够被关闭。
            async with  session.get(url)  as res:  # 需要这个URL资源,获取,
                print (res.status)  # 此处返回为状态码
                print (await  res.text())  # 此处返回为文本信息
    
    url='http://www.baidu.com'
    
    loop=asyncio.get_event_loop()
    loop.run_until_complete(get_html(url))
    loop.close()
    展开全文
  • **同步编程:**任务是按照顺序依次处理,当前这件事情没有彻底做完,下一件事是执行不了的 **异步编程:**当前这件事情没有彻底做完,需要等待一段时间才能继续处理,此时我们不等,继续执行下面的任务,当后面的...

    **同步编程:**任务是按照顺序依次处理,当前这件事情没有彻底做完,下一件事是执行不了的
    **异步编程:**当前这件事情没有彻底做完,需要等待一段时间才能继续处理,此时我们不等,继续执行下面的任务,当后面的任务完成后,再去吧没有彻底完成的事情完成

    【JS的异步编程】
    1.所有的事件绑定都是异步编程 xxx.οnclick=function(){}
    2.所有的定时器都是异步编程 setTimeout(function(){},1000)
    3.AJAX中一般都使用异步编程处理
    4.回调函数也算是异步编程

    【注】=>定时器设定一个时间,到达时间后不一定执行(如果当前还有其他的同步任务正在处理,那么到了时间也得等着)

    =>浏览器是如何规划同步异步机制的
    1.浏览器是多线程的,JS是单线程的(浏览器只给JS执行分配一个线程)
    单线程的特点:一次只能处理一件事
    进程:每一个应用程序都可以理解为一个进程(浏览器打开一个页面,就相当于开辟一个进程),在一个程序中(进程中)我们经常会同时干很多事,此时我们可以分配多个线程去同时完成多项任务。

    2.JS在单线程中实现异步的机制,主要依赖于浏览器的任务队列完成的,浏览器中有两个任务队列,(主任务队列,等待任务队列)

    展开全文
  • 同步编程 - 一个请求对应一个线程。 - 所有都是阻塞执行,所有的执行都在一个线程持续。 - ThreadLocals工作良好。 异步编程 - 一个请求可能会被多个线程去执行。 - 所有都是非阻塞执行,一个被接受的请求可能会被...
  • 一、JS中的同步编程和异步编程: 同步编程:任务是按照顺序依次处理,当前这件事没有彻底做完,下一件事不能执行 异步编程:当前这件事没有彻底做完,需要等待一段时间才能继续处理,此时我们不等,继续执行下面...
  • 二、同步编程和异步编程 // 同步(SYNC)和异步(ASYNC)编程 // JS是属于单线程的,我们最好使用异步编程 // 定时器都是异步编程的 // 事件绑定都是异步编程的 // 回调函数也可以理解为异步编程 // 在AJAX...
  • 同步和异步是一个比较早的概念,大抵在操作系统发明时应该就出现了 例子: 同步:CPU需要计算10个数据,每计算一个结果后,将其写入磁盘,等待写入成功后,再计算下一个数据,直到完成。 异步:CPU需要计算10个...
  • 同步/异步编程的原理

    2019-06-23 15:31:40
    一、JS中的同步编程和异步编程: 同步:任务是按照顺序依次处理的,当前这件事没有彻底做完,下一件事是执行不了的。 异步:当前这件事没有彻底做完,需要等待一段时间才能继续处理,此时我们不等,继续执行下面的...
  • Nodejs同步和异步编程

    2020-05-29 16:48:00
    同步异步代码执行顺序 同步:从上到下依次执行,前面代码会阻塞后面代码的执行。异步:不会等待API执行完成后再向下执行代码。 1、同步代码先执行,按先后顺序执行  也就是先执行console.log('代码开始执行‘)  ...
  • 同步编程就是按照我们写代码的顺序执行代码,而异步编程不按照代码顺序执行,使其编程效率更高。 要想实现异步编程,经常用到回调函数。 回调函数就是告诉你当你这个任务执行完后要干什么,比如setTimeOut(callback,...
  • js同步和异步编程

    2016-06-21 10:10:09
    上一件事情没有完成,继续处理上一件事情,只有上一件事情完成了,才会做下一件事情(js中大部分都是同步编程) for(var i = 0;i;i++){ if(i == 999){ console.log("循环结束!"); } } console.log("ok");
  • 一般情况下,工控机各智能仪表通过RS485总线进行通信。RS485的通信方式是半双工的,只能由作为主节点的工控PC机依次轮询网络上的各智能控制单元子节点。每次通信都是由PC机通过串口向智能控制单元发布命令,智能...
  • 1.异步编程 js是单线程的,所谓线程类似于游戏中的任务线。 一个主要的任务流程,处理我们的js程序,把一些耗时的...2.同步和异步解释 首先搭建一个简易的服务器 app.js代码如下,3s后返回请求。 const express...
  • C#Udp同步和异步编程源代码,代码中有详细注释,在VS2010平台下正常运行,使用网络调试助手调试通过,注意根据自己的电脑修改IP地址
  • get同步/异步 post同步/异步 编程实现
  • 定义:同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)。同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行。异步,和同步相反 调用方不会理解...
  • 编程同步和异步的概念

    千次阅读 2018-07-18 17:03:38
    同步和异步,相信学过编程的都知道,并且经常在使用,但是要表达出来的时候却感觉只能意会不可言传。 平常说的同步和异步一般局限在线程。 同步,并不是按字面意思的同时或一起,而是指协同步调,协助、相互配合。...
  • Promises(CommonJS规范)为异步编程提供统一接口 每个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数 优点:回调函数变成了链式写法,程序的流程可以看的很清楚 缺点:编写理解都相对...
  • 2.异步调用 同步调用:提交完任务后,就在原地等待任务执行完后,拿到结果,再执行下一行代码 同步调用,导致程序串行执行 from concurrent.futures import ThreadPoolExecutor import time import random ...
  • 线程的同步和异步 线程是程序执行流的最小单元,Android每个运行的应用程序可能包含多个线程。 Andorid系统中默认只有一个主线程,也叫UI线程,因此View绘制只能在这个线程内进行,即修改界面的操作只能在主线程中...
  • 继续 Dart 异步编程系列的最后一篇生成器函数。...生成器函数是一种用于延迟生成值序列的函数,并且在 Dart 中生成器函数主要分为两种:同步生成器函数和异步生成器函数。我们知道,比如 int 类型变量(同步)...
  • 在网络通信过程中,通信双方要交换数据,需要高度的协同工作。为了正确的解释信号,接收方必须确切地...通常可以采用同步异步的传输方式对位进行同步处理。 1. 异步传输(Asynchronous Transmission):异步传输模
  • import asyncio from asyncio import Lock, Queue cache = {} lock = Lock() async def get_stuff(url): # lock.acquire()是一个协程 # await lock.acquire() ... # Lock实现了__enter___...
  • 同步和异步的主要区别。 1. 同步的概念 同步就是一直等到消息或返回值后才执行其他的事情。即在等待消息的时候不做其他事情 比如两人约定见面,在等到对方的时候不做其他事情一直等 同步可以理解为单线程。线程...
  • 1.对象锁的同步和异步 同步:synchronized 同步的概念就是共享,我们只需要牢牢把握“共享”这个两个字就可以,如果不是共享的资源,就没有必要进行同步的操作。 异步:asynchronized 异步的概念就是独立,相互之间...
  • C++串口同步和异步的读取与串口设备编程-附件资源

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,974
精华内容 1,989
关键字:

同步编程和异步编程