Skip to content

Latest commit

 

History

History
169 lines (136 loc) · 8.63 KB

同步异步和阻塞非阻塞.md

File metadata and controls

169 lines (136 loc) · 8.63 KB

[TOC]

1.同步异步和阻塞非阻塞

# 真正意义上的 异步IO 是说内核直接将数据拷贝至用户态的内存单元,再通知程序直接去读取数据。
# select / poll / epoll 都是同步IO的多路复用模式

# 1.同步和异步所谓同步就是在发出一个功能调用时在没有得到结果之前该调用就不返回也就是必须一件一件事做,等前一件做完了才能做下一件事。)

# 同步和异步关注的是【消息通信机制】
# 所谓同步,就是在发出一个调用时,没得到结果之前,该调用就不返回,一直等。但是一旦 调用返回 就得到返回值了,【调用者主动等待】这个调用的结果
# 所谓异步,就是在发出一个调用时,这个调用就直接返回了,不管返回有没有结果。当一个异步过程调用发出后,被调用者通过状态,通知来通知调用者,或者通过【回调函数】处理这个调用

# 2.阻塞和非阻塞
# 阻塞和非阻塞关注的是程序线程在【等待调用结果时的状态】
# 阻塞调用是指调用结果返回之前,当前线程会被挂起,但仍是激活的。调用线程只有在得到结果之后才返回
# 非阻塞调用是指在不能立即得到结果之前,该调用不会阻塞当前线程。

# 网络上的例子
#老张爱喝茶,废话不说,煮开水。
#出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
#1 老张把水壶放到火上,立等水开。(同步阻塞);立等就是阻塞了老张去干别的事,老张得一直主动的看着水开没,这就是同步
#2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞);老张去看电视了,这就是非阻塞了,但是老张还是得关注着水开没,这也就是同步了
#3 老张把响水壶放到火上,立等水开。(异步阻塞);立等就是阻塞了老张去干别的事,但是老张不用时刻关注水开没,因为水开了,响水壶会提醒他,这就是异步了
#4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞);老张去看电视了,这就是非阻塞了,而且,等水开了,响水壶会提醒他,这就是异步了
#所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。对应的也就是消息通信机制
#虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。
#所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;对应的也就是程序等待结果时的状态
#看电视的老张,非阻塞。
#情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
相关连接 https://blog.csdn.net/woliuyunyicai/article/details/45165869

2.Python生成器和迭代器

2.1 迭代器

迭代是Python最强大的功能之一,是访问集合元素的一种方式。

迭代器是一个可以记住遍历的位置的对象。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

迭代器有两个基本的方法:iter()next()

字符串,列表或元组对象都可用于创建迭代器:

StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况

2.2 生成器

使用了 yield 的函数被称为生成器(generator)。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

调用的是一个生成器函数,返回的是一个迭代器对象。

2.3 协程优势

协程相对线程的两点优势:

1.协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制中断执行另一个程序,因此,没有线程切换的开销

2.协程不需要多线程锁的机制;因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多

def consumer():

    r = ''
    while True:
        # yield 是个表达式,不仅仅是语句,执行后停在此处,不执行后面的
        n = yield r
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'


def produce(c):
    # 激活生成器函数,这一步必须要有,而且参数只能是None,不能是其他非None例如“”
    c.send(None)
    # 下面继续运行
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        # 此时可以调用迭代器对象的send()使n获得yield返回值“okok”
        r = c.send("okok") # send()跟next()不同,有返回值r (okok)
        # 中断去consumer()
        # consumer()处理完后执行下面
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

# 生成器创建迭代器的对象
c = consumer()
produce(c)
##############################
输出为:
[PRODUCER] Producing 1...
[CONSUMER] Consuming okok...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming okok...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming okok...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming okok...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming okok...
[PRODUCER] Consumer return: 200 OK

3. Python队列Queue

3.1 Python队列是线程级别安全

【简单讲就是线程对共享全局变量的修改的‘不确定性’导致变量混乱,可以通过锁机制解决】

3.2 Python 的 Queue 模块

Queue 模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。这些队列都实现了 锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步

3.3 线程安全级别

引用java里面的Thread线程级别可分为:

  1. 不可变

不变的对象绝对是线程安全的,不需要线程同步,如String、Long、BigInteger

  1. `无条件的线程安全

对象自身做了 足够的内部同步,也不需要外部同步,如 Random 、ConcurrentHashMap、Concurrent集合、atomic

  1. 有条件的线程安全

对象的部分方法可以无条件安全使用,但是有些方法需要外部同步,需要Collections.synchronized;有条件线程安全的最常见的例子是遍历由 Hashtable 或者 Vector 或者返回的迭代器

  1. 非线程安全(线程兼容)

对象本身不提供线程安全机制,但是通过外部同步,可以在并发环境使用, 如ArrayList HashMap

  1. 线程对立

即使外部进行了同步调用,也不能保证线程安全,这种情况非常少,如如System.setOut()、System.runFinalizersOnExit()

4.日志等级分类

日志等级可以分为如下几种 : DEBUG < INFO < WARNING < ERROR < CRITICAL

日志等级(level) 描述
DEBUG 最详细的日志信息,典型应用场景是 问题诊断
INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CRITICAL 当发生严重错误,导致应用程序不能继续运行时记录的信息

logging模块也可以指定日志记录器的日志级别,只有级别大于或等于该指定日志级别的日志记录才会被输出,小于该等级的日志记录将会被丢弃。