在windows下,訊號機制簡單來說是通過背景工作執行緒實現的,該線程運行於相對優先順序THREAD_PRIORITY_HIGHEST,當訊號產生時,windows產生該線程執行訊號處理邏輯,由於該線程優先順序通常主線程,也高於使用者自己顯式建立的任何線程,windows線程調度邏輯將阻塞其餘線程的執行,直到訊號處理完畢背景工作執行緒退出.
以下是測試代碼
#include "stdafx.h"
#include <signal.h>
#include <windows.h>
using namespace std;
int j = 1;
void OnCtrlC(int){
cout << "ctrl + c" << endl;
cout << ::GetCurrentThreadId() << endl;
cout <<::GetThreadPriority(::GetCurrentThread()) << endl;
signal(SIGINT,OnCtrlC);
j = 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
signal(SIGINT,OnCtrlC);
int i = 0;
//::SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
while(true){
Sleep(2000);
cout << ++ i << endl;
cout << ::GetCurrentThreadId() << endl;
cout <<::GetThreadPriority(::GetCurrentThread()) << endl;
// if(j==0)
// break;
}
return 0;
}
該代碼可以進行兩種測試
第一種:如上,該程式運行時按下Ctrl + C後將引起 OnCtrlC函數執行,這種執行可在任何時候發生,甚至在主線程cout << 100 << endl;中僅僅輸出了一個10,然後執行OnCtrlC,在然後將剩下的一個0輸出.程式輸出表明,OnCtrlC輸出的threadid 與 main輸出的threadid不同,並且OnCtrlC輸出的thredid不斷變化,說明改函數的線程每次都是重新建立的,OnCtrlC 調用 GetThreadPriority輸出為 2,正是 THREAD_PRIORITY_HIGHEST,而main輸出0,為 THREAD_PRIORITY_NORMAL.
第二種,注釋掉Sleep(2000),放開main中SetThreadPriority調用,放開
if(j==0)
break;
然後運行,這次將主線程優先順序調到15,高於OnCtrlC的2,因此在程式運行中按Ctrl + C將會發現程式什麼反映也沒有,因為主線程的高優先順序阻止OnCtrlC的執行,這也是為什麼放開
if(j==0)
break;
的原因,假如不放開,高速的迴圈代碼將在你注意到OnCtrlC調用之前滾屏,既然優先順序高於OnCtrlC怎麼又可能OnCtrlC會被調用呢?這是由於windows動態提高線程優先順序機制的作用,簡單來說,就是windows注意到一個線程在3--4秒一直渴望被調度時,將被暫時將優先順序提高到15,這樣與main優先順序相等,大家不分彼此平等競爭,在程式中產生這種情況的辦法是持續不斷按Ctrl + C大約3秒,程式就會退出.
tip:
只所以注釋掉Sleep(2000),因為Sleep函數會將自己的Cpu時間分給其他線程.