成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

 [[414607]]

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

1. 基本模型概述

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

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

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

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

1.用戶點擊鼠標;

2.鼠標驅(qū)動產(chǎn)生鼠標點擊消息(通過中斷實現(xià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ǎo)致后續(xù)的消息無法得到及時響應(yīng),會導(dǎo)致界面卡頓等非常不佳的用戶體驗。

2.2 事件類型

1) 鼠標點擊(單擊、雙擊、右擊)

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; //消息常量標識符(消息號) 
  7.    WPARAM      wParam;  //32位消息特定附加信息 
  8.    LPARAM      lParam;  //32位消息特定附加信息 
  9.    DWORD       time;    //消息創(chuàng)建時的時間 
  10.    POINT       pt;      //消息創(chuàng)建時的光標位置 
  11.    #ifdef _MAC 
  12.    DWORD       lPrivate; 
  13.    #endif 
  14.    } MSG 

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

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

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

  • 窗口消息:與窗口運作有關(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ù)。只有先做好映射表,當消息到達時,消息泵才知道怎么處理該消息。

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.當消息到達時,消息泵知道去調(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)用程序)

消息泵負責從應(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)會把此消息放到目標窗口所在的線程消息隊列中等待處理。

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

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

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

2.7 Windows消息攔截機制

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

此處只做介紹,不做詳細深入。

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

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

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

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

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

3.1 事件類型

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

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

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

4)提交表單。

5)…

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

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

3.2 事件綁定

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

3.3 事件傳播

用戶在點擊div后,事件會按照 捕獲階段、目標階段、冒泡階段的過程進行處理。用戶可以通過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() 會同步地等待消息到達(如果當前沒有任何消息等待被處理)。

該段內(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ù)端角色,一個客戶端角色的兩個進程之間建立通信的完成過程,如下文所述。

4.1.1 服務(wù)端

1)創(chuàng)建SOCKET;

2)綁定IP:Port;

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

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

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)閉連接。

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

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

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

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

4.2事件清單

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

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

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

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

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

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

4.3 事件處理引擎

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

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機制說明

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

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

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

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

超時,事件處理;

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

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

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

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

能用于大量的連接處理。

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 進行處理。

如下圖所示:

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

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

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

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),進行事件處理。

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

5. 總結(jié)

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

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

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

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

6. 參考資料

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

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

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

2017-08-16 10:36:10

JavaScriptNode.js事件驅(qū)動

2012-06-27 14:40:57

事件驅(qū)動擴展

2013-03-28 16:12:12

Message機制應(yīng)用

2011-07-04 14:50:49

QT Event 事件

2013-12-24 10:05:04

memcached

2023-09-27 08:01:14

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

2023-12-28 07:57:27

2021-06-09 07:55:19

NodeEventEmitte驅(qū)動

2022-09-29 08:19:24

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

2017-01-04 18:29:20

AndroidNestedScrol嵌套滑動機制

2023-05-09 08:36:58

DuchesneWindows

2023-02-26 10:59:51

2023-02-19 12:44:07

領(lǐng)域事件DDD

2025-05-20 07:13:22

Spring異步解耦Event

2013-03-26 14:17:21

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

2011-05-31 11:55:00

Android 消息機制

2012-01-04 13:19:46

網(wǎng)絡(luò)

2023-04-28 15:20:37

JavaScript事件循環(huán)

2009-10-20 14:58:15

Javascript事

2019-10-11 09:00:00

JavaScriptEvent Loop前端
點贊
收藏

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

主站蜘蛛池模板: 色爽女| 激情影院久久 | 国产成人精品一区二区三区网站观看 | 国产精品大片在线观看 | 免费v片| 精品美女在线观看视频在线观看 | 午夜欧美a级理论片915影院 | 国产三级国产精品 | 五月天婷婷丁香 | 欧美精品91| 青草福利 | 欧美乱操| 91精品久久久久久久久中文字幕 | 波多野吉衣在线播放 | 亚洲a在线视频 | 日韩午夜激情 | 欧美老少妇一级特黄一片 | 成人不卡| 国产区精品| 国产成人精品网站 | 欧美激情视频一区二区三区免费 | 久久人体视频 | 亚洲一区二区三区免费 | 国产乱码久久久 | 国产毛片av | 国内精品视频一区二区三区 | 在线午夜 | 亚洲一二三视频 | 亚洲 欧美 综合 | cao在线| 91视频在线 | 日韩中文字幕在线观看视频 | 欧美在线| 日日操夜夜操视频 | 午夜成人免费视频 | 久久久久久网站 | 亚洲 欧美 另类 综合 偷拍 | 91福利网 | 成人免费网站www网站高清 | 中文字幕在线看第二 | 综合色影院 |