函數的可重入和C++中Sendmessage 和 PostMessage 的區別

1、什麼是函數的可重入性?
重入一般可以理解爲一個函數在同時多次調用,列如操作系統在進程調度過程中,或者單片機,處理器等的中斷的時候發生衝入的現象


一般可重入的函數必須滿足下列3個條件:
(1)可以在執行的過程中被打斷
(2)被打斷之後,該函數一次調用執行完以前,可以再次被調用
(3)再次調用執行完以後,被打斷的上次調用可以繼續恢復執行,並正確執行


.可重入與線程安全
可重入的定義源於單線程環境。在單線程環境下,一段代碼在執行中可能被硬件中斷,並轉而調用中斷服務程序(ISR)。在本次調用中斷處理函數之前,有可能中斷處理函數已經在執行。因此,任何中斷處理函數都應該是可重入的。
線程安全的概念則源自於多線程環境。可見,他們的起源是不一樣的。那麼,他們沒有什麼必然關係呢。可總結如下:
(1)一個線程安全的函數不一定是可重入的;
(2)一個可重入的函數缺也不一定是線程安全的!


下面這個swap函數是不可重入的:
[cpp] view plain copy print?
int t;  
   
void swap(int *x, int *y)  
{  
    t = *x;  
    *x = *y;  
   
    // hardware interrupt might invoke isr() here!  
    *y = t;  
}  
   
void isr()  
{  
    int x = 1, y = 2;  
    swap(&x, &y);  
}  
既然它不可重入則它一定不是線程安全的。
可以把t改成線程局部變量,使得該函數變成線程安全。然而,這樣修改的話,swap函數依然是不可重入的。例如一個線程已經在執行swap函數,這個時候在同樣的語境下收到硬件中斷,isr()函數會被調用,進而調用swap,swap的不可重入問題就暴露出來了


我們做一定修改,在swap函數裏,在交換前,對此時刻的t全局變量做一個本地的緩存,在交換結束的時候,始終使用該緩存。這樣的話,swap函數在退出的時候,全局變量的之跟進入的時候是一樣的。這樣,就可以保證該函數是線程可重入的。代碼如下:
[cpp] view plain copy print?
int t;  
   
void swap(int *x, int *y)  
{  
    int s;  
   
  <span style="color:#FF0000;">  s = t; // save global variable</span>  
    t = *x;  
    *x = *y;  
   
    // hardware interrupt might invoke isr() here!  
    *y = t;  
<span style="color:#FF0000;">    t = s; // restore global variable</span>  
}  
   
void isr()  
{  
    int x = 1, y = 2;  
    swap(&x, &y);  
}  
2、C++中Sendmessage 和 PostMessage 的區別?


概述
Send有發送的意思,而Post具有投寄的意思。 聯想一下現實生活中我們寫信(很久很久以前。。。。)來記就很簡單了: 
Send:  相當於郵寄員,他會將快件親手交給收件人,並且需要收件人簽字,他才閃人。而在消息機制中,就是說,系統(郵寄員)會將收到的消息(郵局分發)直接發送到某個窗口的窗口過程(收件人),並且需要該窗口作出處理(收件人簽字)才返回。 這東東就是SendMessage。
Post:  相當於郵局、郵筒等等,我們寫好信好,會將信交給郵局,或投寄到郵筒裏,而什麼時候發送,發送到哪裏都由郵局來處理,我們投寄信件的時候,是不會等候這封信件到達收件人手裏,然後纔回家的。
 在消息機制中,就是說,系統(我們)將收到的消息(信件)投寄到應用程序的消息循環(相當於郵筒)中,然後就閃人,具體啥時候處理這條消息(啥時候發送郵件),那就得看“辦事效率”了。


介紹
SendMessage
將指定的消息發送到一個窗口或多個窗口。SendMessage爲這個指定的消息調用窗口處理程序,直到窗口處理完這個程序後纔會返回。和SendMessage函數相比,PostMessage函數發送一個消息到線程消息對列中,並立即返回。
函數原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
參數:
hWnd:其窗口程序將接收消息的窗口的句柄。如果此參數爲HWND_BROADCAST,則消息將被髮送到系統中所有頂層窗口,包括無效或不可見的非自身擁有的窗口、被覆蓋的窗口和彈出式窗口,但消息不被髮送到子窗口。
Msg:指定被髮送的消息。
wParam:指定附加的消息指定信息。
IParam:指定附加的消息指定信息。
返回值:返回值指定消息處理的結果,依賴於所發送的消息。
備註:需要用HWND_BROADCAST通信的應用程序應當使用函數RegisterWindowMessage來爲應用程序間的通信取得一個唯一的消息。
如果指定的窗口是由調用線程創建的,則窗口程序立即作爲子程序調用。如果指定的窗口是由不同線程創建的,則系統切換到該線程並調用恰當的窗口程序。線程間的消息只有在線程執行消息檢索代碼時才被處理。發送線程被阻塞直到接收線程處理完消息爲止。
PostMessage
該函數將一個消息放入(寄送)到與指定窗口創建的線程相聯繫消息隊列裏,不等待線程處理消息就返回。消息隊列裏的消息通過調用GetMessage和PeekMessage取得。
函數原型:B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
參數:
    hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含義的兩個值:
    HWND.BROADCAST:消息被寄送到系統的所有頂層窗口,包括無效或不可見的非自身擁有的窗口、被覆蓋的窗口和彈出式窗口。消息不被寄送到子窗口。
    NULL:此函數的操作和調用參數dwThread設置爲當前線程的標識符PostThreadMessage函數一樣。
    Msg:指定被寄送的消息。
    wParam:指定附加的消息特定的信息。
    IParam:指定附加的消息特定的信息。
返回值:如果函數調用成功,返回非零值:如果函數調用失敗,返回值是零。若想獲得更多的錯誤信息,請調用GetLastError函數。


分析
1, PostMessage只把消息放入隊列,不管其他程序是否處理都返回,然後繼續執行,這是個異步消息投放函數。而SendMessage必須等待其他程序處理消息完了之後才返回,繼續執行,這是個同步消息投放函數。
2, 如果在同一個線程內,PostMessage發送消息時,消息要先放入線程的消息隊列,然後通過消息循環Dispatch到目標窗口。SendMessage發送消息時,系統直接調用目標窗口的消息處理程序,並將結果返回,SendMessage在同一線程中發送消息並不入線程消息隊列。
3,PostMessage的返回值表示PostMessage函數執行是否正確;而SendMessage的返回值表示其他程序處理消息後的返回值。這點大家應該都明白。
4,如果在不同線程內。最好用PostThreadMessage代替PostMessage,他工作的很好,SendMessage發送消息到目標窗口所屬的線程的消息隊列,然後發送消息的線程等待(事實上,他應該還在做一些監測工作,比如監視QS_SENDMESSAGE標誌),直到目標窗口處理完並且結果返回,發送消息的線程才繼續運行。


總結
區別很明顯,SendMessage的消息是不進隊列的,而PostMessage的需要排隊。值得說明的是:雖然一個要進隊,一個不進隊,但是最終處理消息的地方都一樣:都是系統調用窗口過程進行處理(收件人作出反應)。
發佈了57 篇原創文章 · 獲贊 10 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章