Python的语法是简洁的,也是难理解的。
比如yield关键字:
def fun(): for i in range(5): print('test') x = yield i print('good', x) if __name__ == '__main__': a = fun() # print(a.__next__()) # print(a.__next__()) # print(a.__next__()) y = a.send(None) y = a.send(-1) y = a.send(-2) #print(y)
凡是包含yield关键字的函数都不再是一个函数,而成为一个生成器(或迭代器,Generator)
Generator包含一个栈帧(Stack Frame),它维护Generator自身的执行状态,即便生成器的执行被暂停而返回到母程序中,下一次依然能恢复Generator的上一次的状态,从一个yield执行到下一个yield语句前。这就是生成器的执行方式。
迭代一个执行器可以使用send方法,也可以使用__next__方法,send方法能给Generator传递即时的数据,同时接收Generator内部执行状态,这个过程就是协程通信的基础。
而__next__方法只能从Generator接收内部执行状态,而无法向Generator中传入信息,所以__next__不能完成通信,因为通信不能是单向的。
__next__调用下的Generator只是一个迭代器而已。而send调用下的Generator却升级成为一个协程(Co-routine)
使用协程Coroutine的目的是为了避免将业务逻辑的实现混在异步IO的回调函数中,使得程序可读性提升而已。
Python中异步IO采用selectors,它支持了各个操作系统的异步IO的事件管理机制(Linux是epoll),通过selectors.DefaultSelector来实现事件与回调函数的绑定。可以将线程内CPU等待IO的时间转移到操作系统上。毕竟和IO设备交互的不是应用程序,而是操作系统。
多线程:
import threading, time global n class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): global n, lock time.sleep(1) if lock.acquire(): print(n) print(self.name) n += 1 lock.release() if __name__ == '__main__': n = 1 ThreadList = [] lock = threading.Lock() for i in range(1, 200): t = MyThread() ThreadList.append(t) for t in ThreadList: t.start() for t in ThreadList: t.join()
多线程简单,需要注意的就是锁。资源的访问在锁中进行时,锁才会起作用,因此这个锁类似于独立于线程的原子性对象,所有线程都在这个锁上变得有序起来,离开锁重归无序竞争状态。
因此资源无所谓唯一性和不可同时操作性,只有锁自身具备这种性质。所有的资源的同步都是靠代码的约束——将和资源相关的操作和锁绑定在一起才能实现的。多线程在一般设备上效率不逊于协程(单线程内通过操作系统OS进行执行顺序的切换)。