标签:while token 中断 cto 接受 没有 instr 高效 roc
https://www.jianshu.com/p/c4e63927ead2
从python的twisted,到之后Java的NIO,Netty,以及Nodejs带着底层libuv的横空出世,以及现在热议的Golang。异步/多线程/协程编程已经成为了一个耳熟能详的编程范式,它从理论上可以最大化cpu利用率和I/O资源,在计算机核数越来越多,服务器端性能要求越来越高的环境下,熟练掌握异步编程模式已经成为了基本要求。
本文我们来梳理一下异步编程到底是怎么回事。
波粒二象性
一台裸机,配上主板,内存,CPU。这是同步模型还是异步模型?都是,说异步,因为CPU被内部的时钟驱动,每次时钟信号会驱使CPU累进当前读取地址,从内存读出新的内容,接着CPU计算和输出。从这一点看,CPU是被皮鞭不断抽打着前进的异步模式。
然而,它又是同步的,因为皮鞭挥动速度实在太快,i7 CPU频率到了4Ghz,每秒挥动4,000,000,000次。指令如流水一样的按序执行,在逻辑上我们通常把它当做一个同步模型来理解。
是不是有点像光的波粒二象性?
中断
中断是计算机跟外界交互的基础。如果CPU只能累进地址,那么所有的编程模型将都会是同步---如同穿孔纸带那样顺次排列code执行。
操作系统是一个异步系统
操作系统为了方便人操作机器而产生的,它的使命就是接受各种I/O操作产生各种输出,它天然就是一个异步模型。从某种程度上来说,操作系统就像一个大型的中断处理库。
上述方案够吗?不够,因为人总是贪心的,交给操作系统去做切换是非常耗时的操作,另外大量的进程/线程的创建销毁也是不小的开销。程序开发者想直接控制这些过程,而不是交给笨重的操作系统。那怎么办呢?
#Python twisted
class _SignalReactorMixin(object):
def startRunning(self, installSignalHandlers=True):
self._installSignalHandlers = installSignalHandlers
ReactorBase.startRunning(self) def run(self, installSignalHandlers=True):
self.startRunning(installSignalHandlers=installSignalHandlers)
self.mainLoop()
def mainLoop(self):
while self._started:
try:
while self._started:
# Advance simulation time in delayed event
# processors.
self.runUntilCurrent()
t2 = self.timeout()
t = self.running and t2
self.doIteration(t)
except:
log.msg("Unexpected error in main loop.")
log.err()
else: log.msg(‘Main loop terminated.‘)
Nodejs使用Libuv库作为主线程引擎.
Java Netty框架同样自己写了一个EventLoop
//Java Netty
protected void run() {
for (;;) {
try {
int strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
strategy = epollWait(WAKEN_UP_UPDATER.getAndSet(this, 0) == 1);
if (wakenUp == 1) {
Native.eventFdWrite(eventFd.intValue(), 1L);
}
default:
// fallthrough
}
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
if (strategy > 0) {
processReady(events, strategy);
}
runAllTasks();
} else {
final long ioStartTime = System.nanoTime();
if (strategy > 0) {
processReady(events, strategy);
}
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
if (allowGrowing && strategy == events.length()) {
//increase the size of the array as we needed the whole space for the events
events.increase();
}
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
break;
}
}
} catch (Throwable t) {
logger.warn("Unexpected exception in the selector loop.", t);
// Prevent possible consecutive immediate failures that lead to
// excessive CPU consumption.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore.
}
}
}
}
这三个框架的事件引擎实现从本质来说是一样的,就像CPU通过内置的晶振来驱动一样,引擎通过设置短超时时长,高频率的调用select/poll/epoll。I/O事件或者超时返回成为了引擎的驱动力。每次调用返回时,就检查对应的I/O端口上是否有事件,如果有则执行绑定的回调,或者另外开线程处理回调,另外它们还会单独实现一个事件队列用来存储非I/O绑定事件,在每次轮询返回时也会检查该事件队列并处理到期的事件。
假设设置的超时时长是50ms,当任何I/O操作都没有的时候,事件引擎就退化成一个50ms执行一次检查的事件队列。
轮询实质上是同步操作,不过也没办法,因为前面讲过了,程序本身就是同步模型。在低并发量的时候,这种异步框架效率还会低于同步框架,因为浪费了大量CPU时间在轮询等待上,但是在高并发量的时候,每次轮询都会立即返回,几乎变成一个纯异步模型,但不同于直接调用系统API,返回的处理控制逻辑被掌握在引擎手里。因此可以通过很多技术和调整来提高程序效率。
其实从某种程度来说,异步框架是程序试图跳出操作系统界定的同步模型,重新虚拟出一套执行机制,让框架的使用者看起来像一个异步模型。另外通过把很多依赖操作系统实现的笨重功能换到程序内部使用更轻量级的实现。
其实怎么看操作系统都像是累赘了。。。也许有一个完美的解决方案是直接写一个专门的异步式操作系统,可以实现性能的最大化利用。
标签:while token 中断 cto 接受 没有 instr 高效 roc
原文地址:https://www.cnblogs.com/jinanxiaolaohu/p/11691753.html