此文已由作者王荣涛授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
Message loop,即消息循环,在不同系统或者机制下叫法也不尽相同,有被叫做event loop,也有被叫做run loop或者其他名字的,它是一种等待和分派消息的编程结构,是经典的消息驱动机制的基础。为了方便起见,本文对各系统下类似的结构统称为message loop。
结构
Message loop,顾名思义,首先它是一种循环,这和我们初学C语言时接触的for、while是同一种结构。
在Windows下它可能是这个样子的:
MSG msg;BOOL bRet; ...while (bRet = ::GetMessage(&msg, NULL, 0, 0)) { if (bRet == -1) { // Handle Error } else { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } }
在iOS下它可能是这个样子的:
BOOL shouldQuit = NO; ...BOOL ok = YES; NSRunLoop *loop = [NSRunLoop currentRunLoop];while (ok && !shouldQuit) { ok = [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; }
而用libuv实现的I/O消息循环则可能是这样:
bool should_quit = false; ... uv_loop_t *loop = ...while (!should_quit) { uv_run(loop, UV_RUN_ONCE); }
在其他系统或机制下,它还有各自独特的实现,但都大体相似。
事实上,正常运行过程中在接到特殊消息或者指令之前,它就是一个彻底的死循环!同时,这样的结构也决定了它更多意义上是一种单线程上的设计。也正因为如此,对这种编程结构进行了封装的系统(比如iOS)也往往不保证或者根本不屑于提及其线程安全性。而多线程共享的消息循环在笔者看来在绝大部分场景下都属于逆天的设计,本文只讨论单线程上的消息循环。
Loop前面有个定语message,进一步表明它要处理的对象,即消息。这里说的消息是广义上的消息,它可能是UI消息、通知、I/O事件等等。那么消息从哪里来?消息循环又从哪里提取它们?这在不同系统或机制下有所不同:有来自消息队列的,有来自输入源/定时器源的,有来自异步网络、文件完成操作通知的,还有来自可观察对象状态变化的等等。这里把消息循环提取消息的源统称为消息源,简称源。
消息产生后源不会也无法主动推给消息循环。以Windows消息为例,一条异步窗口消息产生后它会被存放在窗口所属线程的消息队列上,如果消息循环不采取任何措施,那么它将永远无法被处理。消息循环从消息队列中去抽取,它才能被取出并分派。这种从消息队列中抽取