自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于消息的事件驅(qū)動機(jī)制(Message Based, Event Driven)

開發(fā) 前端
基于消息的事件驅(qū)動機(jī)制是一個通用模型,廣泛應(yīng)用于桌面軟件開發(fā)、網(wǎng)絡(luò)應(yīng)用程序開發(fā)、前端開發(fā)等技術(shù)方向中。本文主要描述基本模型、基本框架,用于說明不同技術(shù)的共性知識。

 [[414607]]

本文轉(zhuǎn)載自微信公眾號「一個程序員的修煉之路」,作者地下潛行者。轉(zhuǎn)載本文請聯(lián)系一個程序員的修煉之路公眾號。

1. 基本模型概述

基于消息的事件驅(qū)動機(jī)制是一個通用模型,廣泛應(yīng)用于桌面軟件開發(fā)、網(wǎng)絡(luò)應(yīng)用程序開發(fā)、前端開發(fā)等技術(shù)方向中。本文主要描述基本模型、基本框架,用于說明不同技術(shù)的共性知識??梢岳斫鉃橥獠坎僮魇录?,被轉(zhuǎn)化為消息存放于隊列中;而每種類型的消息都有對應(yīng)的處理;通過消息循環(huán),完成讀消息、調(diào)用消息處理這個過程。這個過程,只要應(yīng)用不退出,會一直進(jìn)行下去。下圖的模型從Windows應(yīng)用程序而來,但是具有一定的通用性。

2. 模型在MFC程序中的應(yīng)用

MFC(Microsoft Foundation Classes)是微軟的基礎(chǔ)類庫,對大部分的Windows API進(jìn)行封裝,同時也是桌面軟件的UI開發(fā)框架,下圖是一個用VS2019自動生成的MFC多文檔應(yīng)用。不用做任何開發(fā)工作,就可以得到一個自帶菜單欄、工具欄、狀態(tài)欄、屬性展示框等豐富的界面框架。不過現(xiàn)在MFC已經(jīng)沒落,除了歷史項目,已經(jīng)很少有新項目,采用MFC。下文會基于鼠標(biāo)點擊后完整的系統(tǒng)響應(yīng)過程,說明該模型在MFC中的體現(xiàn)。

2.1 從鼠標(biāo)點擊到響應(yīng)處理的完整過程

1.用戶點擊鼠標(biāo);

2.鼠標(biāo)驅(qū)動產(chǎn)生鼠標(biāo)點擊消息(通過中斷實現(xiàn)),進(jìn)行系統(tǒng)消息隊列;

3.系統(tǒng)消息轉(zhuǎn)換為應(yīng)用程序消息,放入應(yīng)用程序隊列;

4.消息泵從應(yīng)用程序消息隊列中讀取消息;

5.消息派發(fā)及處理,借助USER模塊,將消息派發(fā)至對應(yīng)窗口的對應(yīng)消息處理函數(shù);

問題:為什么消息處理函數(shù)中不能做長耗時的任務(wù)?

消息泵處理消息時是依次處理,處理完一條消息后,再處理下一條消息。如果當(dāng)前消息的處理事件過長,會導(dǎo)致后續(xù)的消息無法得到及時響應(yīng),會導(dǎo)致界面卡頓等非常不佳的用戶體驗。

2.2 事件類型

1) 鼠標(biāo)點擊(單擊、雙擊、右擊)

2) 鍵盤按鍵

3) 用戶在觸摸屏上的點擊事件

4) …

用戶在電腦上的各種操作,對應(yīng)到各種事件類型、不同的事件類型,會被轉(zhuǎn)換為不同的消息。

2.3 消息定義

用戶操作事件,會被轉(zhuǎn)化為消息。消息定義如下:

  1. /* 
  2.  * Message structure 
  3.  */ 
  4.    typedef struct tagMSG { 
  5.    HWND        hwnd;    //接受消息的窗口句柄 
  6.    UINT        message; //消息常量標(biāo)識符(消息號) 
  7.    WPARAM      wParam;  //32位消息特定附加信息 
  8.    LPARAM      lParam;  //32位消息特定附加信息 
  9.    DWORD       time;    //消息創(chuàng)建時的時間 
  10.    POINT       pt;      //消息創(chuàng)建時的光標(biāo)位置 
  11.    #ifdef _MAC 
  12.    DWORD       lPrivate; 
  13.    #endif 
  14.    } MSG 

微軟有提供一系列的消息定義,用戶也可以自定義消息,進(jìn)行應(yīng)用程序的開發(fā)。

windows 消息類型可以分為以下兩大類:

(1)系統(tǒng)消息:范圍在[0x0000,0x03ff]之間,細(xì)分為三小類:

  • 窗口消息:與窗口運(yùn)作有關(guān),窗口創(chuàng)建,窗口繪制,窗口移動,窗口銷毀;
  • 命令消息:一般指WM_COMMAND消息,與處理用戶請求有關(guān),通常由控件或者菜單產(chǎn)生。
  • 通知消息:特指WM_NOTIFY消息。通常指一個窗口內(nèi)的子控件發(fā)生了一些事情,需要通知父窗口。

微軟官方鏈接,給出了系統(tǒng)消息的范圍:

The system reserves message-identifier values in the range 0x0000 through 0x03FF (the value of WM_USER – 1) for system-defined messages. Applications cannot use these values for private messages.

(2)應(yīng)用定義的消息

  • WM_USER : 【0X0400-0X7FFF】, 用戶自定義的消息范圍。
  • WM_APP : 【0X8000-0XBFFF】,用于程序之間的消息通信。
  • RegisterWindowMessage :【0XC000-0XFFFF】

微軟官方內(nèi)容,給出了應(yīng)用消息的取值范圍:

Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message identifiers for private window classes.

If your application is marked version 4.0, you can use message-identifier values in the range 0x8000 (WM_APP) through 0xBFFF for private messages.

The system returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the RegisterWindowMessage function to register a message. The message identifier returned by this function is guaranteed to be unique throughout the system. Use of this function prevents conflicts that can arise if other applications use the same message identifier for different purposes.

2.4 消息處理映射表(事件處理綁定)

消息處理映射表指每個消息對應(yīng)的處理函數(shù)。只有先做好映射表,當(dāng)消息到達(dá)時,消息泵才知道怎么處理該消息。

2.4.1 Win32應(yīng)用程序中的消息處理映射表

WndProc為消息處理函數(shù),代碼內(nèi)部通過switch case,給不同的消息指定不同的處理函數(shù)。

  1. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
  2.     switch (message) 
  3.     { 
  4.     case WM_COMMAND: 
  5.         { 
  6.             int wmId = LOWORD(wParam); 
  7.             // 分析菜單選擇: 
  8.             switch (wmId) 
  9.             { 
  10.             case IDM_ABOUT: 
  11.                 DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); 
  12.                 break; 
  13.             case IDM_EXIT: 
  14.                 DestroyWindow(hWnd); 
  15.                 break; 
  16.             default
  17.                 return DefWindowProc(hWnd, message, wParam, lParam); 
  18.             } 
  19.         } 
  20.         break; 
  21.     case WM_PAINT: 
  22.         { 
  23.             PAINTSTRUCT ps; 
  24.             HDC hdc = BeginPaint(hWnd, &ps); 
  25.             // TODO: 在此處添加使用 hdc 的任何繪圖代碼... 
  26.             EndPaint(hWnd, &ps); 
  27.         } 
  28.         break; 
  29.     case WM_DESTROY: 
  30.         PostQuitMessage(0); 
  31.         break; 
  32.     default
  33.         return DefWindowProc(hWnd, message, wParam, lParam); 
  34.     } 
  35.     return 0; 

2.4.2 MFC中的消息處理映射表

在如下代碼中可以看到,WINDOWS消息WM_CREATE,對應(yīng)的消息處理函數(shù)為OnCreate.當(dāng)消息到達(dá)時,消息泵知道去調(diào)用OnCreate函數(shù)。

宏BEGIN_MESSAGE_MAP,END_MESSAGE_MAP就是用于定義消息映射表的。

  1. BEGIN_MESSAGE_MAP(CFileView, CDockablePane) 
  2.   ON_WM_CREATE() 
  3.   ... 
  4. END_MESSAGE_MAP() 
  5.  
  6. #define ON_WM_CREATE() \ 
  7.   { WM_CREATE, 0, 0, 0, AfxSig_is, \ 
  8.     (AFX_PMSG) (AFX_PMSGW) \ 
  9.     (static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: OnCreate)) }, 

2.5 消息泵(Windows應(yīng)用程序)

消息泵負(fù)責(zé)從應(yīng)用程序的消息隊列中讀取消息、轉(zhuǎn)換消息、派發(fā)消息。

  1. MSG msg; 
  2.  
  3.  // 主消息循環(huán): 
  4. while (GetMessage(&msg, nullptr, 0, 0)) 
  5.      if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
  6.      { 
  7.          TranslateMessage(&msg); 
  8.          DispatchMessage(&msg); 
  9.      } 

以上出現(xiàn)的函數(shù)都是Windows API 函數(shù)

  • GetMessage 從消息隊列中讀取消息
  • TranslateMessage 消息翻譯、轉(zhuǎn)換。
  • DispatchMessage 派發(fā)消息、找到消息對應(yīng)的窗口、調(diào)用響應(yīng)函數(shù)

2.6 消息隊列

(1)系統(tǒng)消息隊列:這是系統(tǒng)唯一隊列,設(shè)備驅(qū)動把用戶的操作輸入轉(zhuǎn)化成消息存放于系統(tǒng)隊列中,然后系統(tǒng)會把此消息放到目標(biāo)窗口所在的線程消息隊列中等待處理。

(2)線程消息隊列:每一個GUI線程都會維護(hù)一個線程消息隊列,然后線程消息隊列中的消息會被送到相應(yīng)的窗口過程處理。

消息隊列并不可以直接訪問,但是我們可以通過指定接口去訪問消息隊列。

  • PostMessage函數(shù),用于向消息隊列中追加消息,并立即返回;
  • GetMessage函數(shù),用于從消息隊列中讀取消息;

2.7 Windows消息攔截機(jī)制

上文介紹Windows消息的產(chǎn)生、讀取、派發(fā)處理等,其實用戶可以通過Windows的消息攔截機(jī)制,對消息到達(dá)目標(biāo)窗體之前進(jìn)行提前處理。這主要通過Windows的Hook機(jī)制實現(xiàn)。常用的調(diào)試工具SPY++,就是利用HOOK機(jī)制截獲窗口消息。

此處只做介紹,不做詳細(xì)深入。

2.8 模態(tài)對話框和非模態(tài)對話框的區(qū)別

模態(tài)對話框:在子界面活動期間,父窗口是無法進(jìn)行消息響應(yīng)。獨(dú)占用戶輸入

  • 非模態(tài)對話框:各窗口之間不影響。
  • 模態(tài)對話框通過在消息循環(huán)內(nèi)再造消息循環(huán)。如果當(dāng)前窗口內(nèi)的消息循環(huán)不退出,父窗口的消息循環(huán)將無法運(yùn)轉(zhuǎn),也即無法響應(yīng)。從而產(chǎn)生模態(tài)對話框獨(dú)占響應(yīng)的效果。

3. 模型在瀏覽器中的應(yīng)用

在網(wǎng)頁應(yīng)用程序開發(fā)中(前端開發(fā)),用戶的點擊操作產(chǎn)生事件,同時在網(wǎng)頁應(yīng)用程序中進(jìn)行處理響應(yīng)。瀏覽器應(yīng)用,同樣適用于該模型。

3.1 事件類型

1)用戶在某個元素上點擊鼠標(biāo)或懸停光標(biāo)。

2)用戶在鍵盤中按下某個按鍵。

3)用戶調(diào)整瀏覽器的大小或者關(guān)閉瀏覽器窗口。

4)提交表單。

5)…

完整的瀏覽器事件清單,可以參考如下鏈接:

https://developer.mozilla.org/en-US/docs/Web/Events

3.2 事件綁定

在如下示例中,對HTML的DOM元素中進(jìn)行事件綁定,增加了click事件響應(yīng)。當(dāng)用戶點擊該div的時候,響應(yīng)函數(shù)就會執(zhí)行。瀏覽器中有多種事件綁定方式,此處只用addEventListener,作為示例說明。

3.3 事件傳播

用戶在點擊div后,事件會按照 捕獲階段、目標(biāo)階段、冒泡階段的過程進(jìn)行處理。用戶可以通過addEventListener中useCapture字段,決定事件的捕獲階段。

  • true - 捕獲階段執(zhí)行事件響應(yīng)函數(shù)
  • false- 冒泡階段執(zhí)行事件響應(yīng)函數(shù)

3.4 事件循環(huán)

事件循環(huán)之所以稱之為事件循環(huán),是因為它經(jīng)常按照類似如下的方式來被實現(xiàn):

  1. while (queue.waitForMessage()) { 
  2.   queue.processNextMessage(); 

queue.waitForMessage() 會同步地等待消息到達(dá)(如果當(dāng)前沒有任何消息等待被處理)。

該段內(nèi)容來自于鏈接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop

3.5 任務(wù)隊列

Javascript腳本的執(zhí)行環(huán)境是單線程的,所以必定存在一個任務(wù)隊列用于依次存放待響應(yīng)任務(wù)。即3.4章節(jié)中的queue.

4. 模型在網(wǎng)絡(luò)應(yīng)用程序中的應(yīng)用

4.1 點對點的網(wǎng)絡(luò)應(yīng)用程序工作過程

一個服務(wù)端角色,一個客戶端角色的兩個進(jìn)程之間建立通信的完成過程,如下文所述。

4.1.1 服務(wù)端

1)創(chuàng)建SOCKET;

2)綁定IP:Port;

3)SOCKET進(jìn)入監(jiān)聽模式;

4)等待外部連接請求進(jìn)入,如果有,建立連接;

5)數(shù)據(jù)讀寫處理;

6)處理結(jié)束,關(guān)閉連接。

4.1.2 客戶端

1)創(chuàng)建SOCKET;

2)向指定的IP:Port發(fā)起連接請求,并建立連接;

3)發(fā)送數(shù)據(jù)/接收數(shù)據(jù);

4)處理結(jié)束,關(guān)閉連接。

問題:當(dāng)一臺機(jī)器有10W,乃至更多的并發(fā)網(wǎng)絡(luò)連接,如何處理?

一個線程處理一個SOCKET連接?(大量的線程,會導(dǎo)致CPU資源花在線程切換上,而不是真正的有效工作)

通過SELECT周期性輪詢所有SOCKET,檢查是否可讀、可寫?(主動遍歷所有SOCKET集合,當(dāng)SOCKET基數(shù)特別大、活躍量少的時候,低效。SELECT本身也有數(shù)量限制)

通過事件通知,只處理活躍的局部少量SOCKET (參考CPU中斷處理、高效)

4.2事件清單

網(wǎng)絡(luò)應(yīng)用程序中存在一些基本的事件以及圍繞這些事件開展的處理。在陳碩的書籍《Linux多線程服務(wù)器端編程》有介紹三個半事件。

1)連接建立,包含服務(wù)端接收新連接、客戶端發(fā)起連接;

2)連接斷開,包括主動斷開、被動斷開;阿

3)消息到達(dá),表示有數(shù)據(jù)到緩沖區(qū),可以讀,拷貝到用戶自己控制的緩沖區(qū)中;

4)消息發(fā)送完畢,算半個事件。

開發(fā)人員應(yīng)針對指定事件,開發(fā)對應(yīng)的處理函數(shù),并通過引擎完成事件處理。

4.3 事件處理引擎

目前操作系統(tǒng)層面提供了高效的網(wǎng)絡(luò)通信處理機(jī)制,不同的語言也提供了各種類庫。

4.3.1 操作系統(tǒng)層支持

1)Windows IOCP

2)CentOS Epoll

3)xxxBSD kqueue

4.3.2 語言層面的框架支持

1)C/C++ libevent/Muduo/Asio/…

2)Java Netty

3)DotNet DotNetty

4.3.3 Epoll機(jī)制說明

1)創(chuàng)建Epoll實例句柄:可以理解為管理其他socket的領(lǐng)頭羊;

2)事件注冊:為每個SOCKET要關(guān)注的事件進(jìn)行注冊,服務(wù)端監(jiān)聽SOCKET

主要關(guān)注有沒有新的連接進(jìn)來;

一般性SOCKET關(guān)注是否有數(shù)據(jù)進(jìn)來,需要讀取;

超時,事件處理;

3)進(jìn)入等待狀態(tài),有事件進(jìn)來時,操作系統(tǒng)會進(jìn)行通知;

4)事件處理,根據(jù)操作系統(tǒng)的通知,應(yīng)用程序進(jìn)行反饋,調(diào)用對應(yīng)事件的處理函數(shù)進(jìn)行響應(yīng)。

由于操作系統(tǒng)層面的支持,系統(tǒng)反饋時,只對活躍的SOCKET進(jìn)行處理,數(shù)據(jù)量少,檢查量少,處理量也少。因此可以處理大量socket并發(fā)。

能夠這么做,是因為網(wǎng)絡(luò)應(yīng)用程序進(jìn)行數(shù)據(jù)收發(fā),必然存在網(wǎng)絡(luò)延遲,所以才可以這么處理。如果每個SOCKET都是滿負(fù)荷運(yùn)作,那么這種機(jī)制也不

能用于大量的連接處理。

4.3.4 Muduo網(wǎng)絡(luò)庫說明

Muduo是由陳碩編寫的,基于Epoll,采用Reactor模式開發(fā)的開源網(wǎng)絡(luò)通信庫。

Reactor模式稱為反應(yīng)堆模型,是指有一個循環(huán)的過程,不斷監(jiān)聽對應(yīng)事件是否觸發(fā),事件觸發(fā)時調(diào)用對應(yīng)的 callback 進(jìn)行處理。

如下圖所示:

所有的客戶端連接請求事件都由acceptor處理,并建立新的連接;

所有已建立的連接,按照讀數(shù)據(jù)、解碼、處理、編碼、數(shù)據(jù)發(fā)送返回的過程進(jìn)行處理。其中數(shù)據(jù)讀寫,由反應(yīng)堆根據(jù)事件進(jìn)行處理。

Muduo的詳細(xì)說明,可以參考如下文檔:

https://www.cyhone.com/articles/analysis-of-muduo/

4.3.5 基于Muduo的網(wǎng)絡(luò)應(yīng)用程序開發(fā)模式

1)建立一個事件循環(huán)器EventLoop(也可以理解為消息泵)

2)建立對應(yīng)的服務(wù)器TcpServer

3)設(shè)置TcpServer的Callback(可以理解為建立事件處理映射表)

4)啟動server

5)開啟事件循環(huán),進(jìn)行事件處理。

此處的消息隊列,可以理解為由操作系統(tǒng)返回的待處理SOCKET及其對應(yīng)事件的清單。

5. 總結(jié)

通過上文可以看出,在不同的技術(shù)方向上,其實是可以挖掘出通性技術(shù),并進(jìn)行學(xué)習(xí)的。因此我做了如下歸納:

1)不同技術(shù),采用類似設(shè)計思路

2)研究共性,便于知識觸類旁通

3)細(xì)節(jié)差異,通過工程實踐掌握

6. 參考資料

1. 微軟官方關(guān)于消息及其隊列的介紹: https://docs.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues#application-defined-messages

2. Muduo細(xì)節(jié): https://github.com/chenshuo/muduo

責(zé)任編輯:武曉燕 來源: 一個程序員的修煉之路
相關(guān)推薦

2017-08-16 10:36:10

JavaScriptNode.js事件驅(qū)動

2012-06-27 14:40:57

事件驅(qū)動擴(kuò)展

2011-07-04 14:50:49

QT Event 事件

2013-03-28 16:12:12

Message機(jī)制應(yīng)用

2023-12-28 07:57:27

2013-12-24 10:05:04

memcached

2023-09-27 08:01:14

數(shù)據(jù)推送事件

2021-06-09 07:55:19

NodeEventEmitte驅(qū)動

2022-09-29 08:19:24

數(shù)據(jù)庫運(yùn)維D-SMART

2023-05-09 08:36:58

DuchesneWindows

2017-01-04 18:29:20

AndroidNestedScrol嵌套滑動機(jī)制

2023-02-19 12:44:07

領(lǐng)域事件DDD

2023-02-26 10:59:51

2013-03-26 14:17:21

架構(gòu)架構(gòu)設(shè)計事件驅(qū)動

2012-01-04 13:19:46

網(wǎng)絡(luò)

2011-05-31 11:55:00

Android 消息機(jī)制

2023-04-28 15:20:37

JavaScript事件循環(huán)

2019-10-11 09:00:00

JavaScriptEvent Loop前端

2023-11-02 08:01:22

2009-10-20 14:58:15

Javascript事
點贊
收藏

51CTO技術(shù)棧公眾號