事件驱动

Windows是基于事件驱动的.
一般来讲, 服务器处理模型有三种:
1每收到一个请求,创建一个新的进程,来处理该请求;
2每收到一个请求,创建一个新的线程,来处理该请求;
3每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求

1 开销较大:每处理一个事件就要新建一个进程,开销太大,但是实现起来很简单
2 死锁问题:多线程调节容易遇到死锁问题,程序直接卡死
3 逻辑复杂

那么事件驱动的优势在哪里呢。
打个比方,我们要获取一个鼠标点击的动作。
如果我们通过创建线程或者进程的方法,去循环扫描当前是否有鼠标点击事件那么可能会造成资源浪费(鼠标一直不点击,但仍在进行扫描),响应缓慢(扫描的设备有很多,会造成响应缓慢的问题)等问题。
但是事件驱动就不一样了,它的核心原理是以消息队列为核心,当捕获到一个事件(如鼠标点击)时把他放进消息队列,然后当该事件从队列中被取出时,根据事件类型调用不同的函数来进行处理,其中每个事件一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数 。

消息机制

事件驱动在Windows下的具象化就是消息机制。
事件队列对应过来就是消息队列.

它会为每一个应用程序新开一个对应的消息队列,用于捕获其消息。

我们以一个简单的窗口实现来看看消息机制。 注意下方注释处即可。

#include<Windows.h>


LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);


int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInStance, LPSTR lpCmdLine, int nShowCmd) {

static TCHAR szAppName[] = TEXT("窗口类名称");
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass = { 0 };

//设计窗口类
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hinstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;


if (!RegisterClassEx(&wndclass))
{
MessageBox(NULL, TEXT("RegisterClassEx failed!"),TEXT("title"), MB_ICONERROR);
return 0;
}

hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
szAppName,
TEXT("窗口名称"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hinstance,
NULL);

ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);

while (GetMessage(&msg, hwnd, 0, 0)) { #从消息队列中获取消息,若存在消息待处理,则进行窗口过程。同时通过句柄得到需要捕捉消息的应用程序。
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
RECT rect;

switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("FUCK"), -1, &rect, DT_CENTER);
EndPaint(hwnd, &ps);
return 0;

case WM_LBUTTONUP:
MessageBox(NULL, TEXT("老子被点了"), TEXT("tick"), 0);
return 0;
}

return DefWindowProc(hwnd, message, wParam, lParam);
}

对于消息机制,有几个点需要特别关注。

1.WM_PAINT,WM_TIMER,WM_QUIT 这几个消息永远被放在消息队列最后。
究其原因很简单,就拿WM_QUIT举例,他是意思是退出,若不放在最后,WM_QUIT后面的消息也就无法处理了。
2.也有部分消息是非队列消息,可以无视队列顺序首先被处理。