串口API通信函數(shù)編程
發(fā)展壯大離不開廣大客戶長期以來的信賴與支持,我們將始終秉承“誠信為本、服務(wù)至上”的服務(wù)理念,堅持“二合一”的優(yōu)良服務(wù)模式,真誠服務(wù)每家企業(yè),認(rèn)真做好每個細(xì)節(jié),不斷完善自我,成就企業(yè),實現(xiàn)共贏。行業(yè)涉及成都主動防護(hù)網(wǎng)等,在網(wǎng)站建設(shè)公司、網(wǎng)絡(luò)營銷推廣、WAP手機(jī)網(wǎng)站、VI設(shè)計、軟件開發(fā)等項目上具有豐富的設(shè)計經(jīng)驗。
16位串口應(yīng)用程序中,使用的16位的Windows API通信函數(shù):
①OpenComm()打開串口資源,并指定輸入、輸出緩沖區(qū)的大?。ㄒ宰止?jié)計)
CloseComm() 關(guān)閉串口;
例:int idComDev;
idComDev = OpenComm("COM1", 1024, 128);
CloseComm(idComDev);
②BuildCommDCB() 、setCommState()填寫設(shè)備控制塊DCB,然后對已打開的串口進(jìn)行參數(shù)配置; 例:DCB dcb;
BuildCommDCB("COM1:2400,n,8,1", dcb);
SetCommState(dcb);
③ ReadComm 、WriteComm()對串口進(jìn)行讀寫操作,即數(shù)據(jù)的接收和發(fā)送.
例:char *m_pRecieve; int count;
ReadComm(idComDev,m_pRecieve,count);
Char wr[30]; int count2;
WriteComm(idComDev,wr,count2);
16位下的串口通信程序最大的特點就在于:串口等外部設(shè)備的操作有自己特有的API函數(shù);而32位程序則把串口操作(以及并口等)和文件操作統(tǒng)一起來了,使用類似的操作。
在MFC下的32位串口應(yīng)用程序
32位下串口通信程序可以用兩種方法實現(xiàn):利用ActiveX控件;使用API 通信函數(shù)。
使用ActiveX控件,程序?qū)崿F(xiàn)非常簡單,結(jié)構(gòu)清晰,缺點是欠靈活;使用API 通信函數(shù)的優(yōu)缺點則基本上相反。
使用ActiveX控件:
VC++ 6.0提供的MSComm控件通過串行端口發(fā)送和接收數(shù)據(jù),為應(yīng)用程序提供串行通信功能。使用非常方便,但可惜的是,很少有介紹MSComm控件的資料。
⑴.在當(dāng)前的Workspace中插入MSComm控件。
Project菜單------Add to Project----Components and Controls-----Registered
ActiveX Controls---選擇Components: Microsoft Communications Control,
version 6.0 插入到當(dāng)前的Workspace中。
結(jié)果添加了類CMSComm(及相應(yīng)文件:mscomm.h和mscomm.cpp )。
⑵.在MainFrm.h中加入MSComm控件。
protected:
CMSComm m_ComPort;
在Mainfrm.cpp::OnCreare()中:
DWORD style=WS_VISIBLE|WS_CHILD;
if (!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
TRACE0("Failed to create OLE Communications Control\n");
return -1; // fail to create
}
⑶.初始化串口
m_ComPort.SetCommPort(1); //選擇COM?
m_ComPort. SetInBufferSize(1024); //設(shè)置輸入緩沖區(qū)的大小,Bytes
m_ComPort. SetOutBufferSize(512); //設(shè)置輸入緩沖區(qū)的大小,Bytes//
if(!m_ComPort.GetPortOpen()) //打開串口
m_ComPort.SetPortOpen(TRUE);
m_ComPort.SetInputMode(1); //設(shè)置輸入方式為二進(jìn)制方式
m_ComPort.SetSettings("9600,n,8,1"); //設(shè)置波特率等參數(shù)
m_ComPort.SetRThreshold(1); //為1表示有一個字符引發(fā)一個事件
m_ComPort.SetInputLen(0);
⑷.捕捉串口事項。MSComm控件可以采用輪詢或事件驅(qū)動的方法從端口獲取數(shù)據(jù)。我們介紹比較使用的事件驅(qū)動方法:有事件(如接收到數(shù)據(jù))時通知程序。在程序中需要捕獲并處理這些通訊事件。
在MainFrm.h中:
protected:
afx_msg void OnCommMscomm();
DECLARE_EVENTSINK_MAP()
在MainFrm.cpp中:
BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd )
ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE) //映射ActiveX控件事件
END_EVENTSINK_MAP()
⑸.串口讀寫. 完成讀寫的函數(shù)的確很簡單,GetInput()和SetOutput()就可。兩個函數(shù)的原型是:
VARIANT GetInput();及 void SetOutput(const VARIANT newValue);都要使用VARIANT類型(所有Idispatch::Invoke的參數(shù)和返回值在內(nèi)部都是作為VARIANT對象處理的)。
無論是在PC機(jī)讀取上傳數(shù)據(jù)時還是在PC機(jī)發(fā)送下行命令時,我們都習(xí)慣于使用字符串的形式(也可以說是數(shù)組形式)。查閱VARIANT文檔知道,可以用BSTR表示字符串,但遺憾的是所有的BSTR都是包含寬字符,即使我們沒有定義_UNICODE_UNICODE也是這樣! WinNT支持寬字符, 而Win95并不支持。為解決上述問題,我們在實際工作中使用CbyteArray,給出相應(yīng)的部分程序如下:
void CMainFrame::OnCommMscomm(){
VARIANT vResponse; int k;
if(m_commCtrl.GetCommEvent()==2) {
k=m_commCtrl.GetInBufferCount(); //接收到的字符數(shù)目
if(k0) {
vResponse=m_commCtrl.GetInput(); //read
SaveData(k,(unsigned char*) vResponse.parray-pvData);
} // 接收到字符,MSComm控件發(fā)送事件 }
。。。。。 // 處理其他MSComm控件
}
void CMainFrame::OnCommSend() {
。。。。。。。。 // 準(zhǔn)備需要發(fā)送的命令,放在TxData[]中
CByteArray array;
array.RemoveAll();
array.SetSize(Count);
for(i=0;iCount;i++)
array.SetAt(i, TxData[i]);
m_ComPort.SetOutput(COleVariant(array)); // 發(fā)送數(shù)據(jù) }
二 使用32位的API 通信函數(shù):
⑴.在中MainFrm.cpp定義全局變量
HANDLE hCom; // 準(zhǔn)備打開的串口的句柄
HANDLE hCommWatchThread ;//輔助線程的全局函數(shù)
⑵.打開串口,設(shè)置串口
hCom =CreateFile( "COM2", GENERIC_READ | GENERIC_WRITE, // 允許讀寫
0, // 此項必須為0
NULL, // no security attrs
OPEN_EXISTING, //設(shè)置產(chǎn)生方式
FILE_FLAG_OVERLAPPED, // 我們準(zhǔn)備使用異步通信
NULL );
我使用了FILE_FLAG_OVERLAPPED結(jié)構(gòu)。這正是使用API實現(xiàn)非阻塞通信的關(guān)鍵所在。
ASSERT(hCom!=INVALID_HANDLE_VALUE); //檢測打開串口操作是否成功
SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//設(shè)置事件驅(qū)動的類型
SetupComm( hCom, 1024,512) ; //設(shè)置輸入、輸出緩沖區(qū)的大小
PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
| PURGE_RXCLEAR ); //清干凈輸入、輸出緩沖區(qū)
COMMTIMEOUTS CommTimeOuts ; //定義超時結(jié)構(gòu),并填寫該結(jié)構(gòu)
…………
SetCommTimeouts( hCom, CommTimeOuts ) ;//設(shè)置讀寫操作所允許的超時
DCB dcb ; // 定義數(shù)據(jù)控制塊結(jié)構(gòu)
GetCommState(hCom, dcb ) ; //讀串口原來的參數(shù)設(shè)置
dcb.BaudRate =9600; dcb.ByteSize =8; dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT ;dcb.fBinary = TRUE ;dcb.fParity = FALSE;
SetCommState(hCom, dcb ) ; //串口參數(shù)配置
上述的COMMTIMEOUTS結(jié)構(gòu)和DCB都很重要,實際工作中需要仔細(xì)選擇參數(shù)。
⑶啟動一個輔助線程,用于串口事件的處理。
Windows提供了兩種線程,輔助線程和用戶界面線程。輔助線程沒有窗口,所以它沒有自己的消息循環(huán)。但是輔助線程很容易編程,通常也很有用。
在次,我們使用輔助線程。主要用它來監(jiān)視串口狀態(tài),看有無數(shù)據(jù)到達(dá)、通信有無錯誤;而主線程則可專心進(jìn)行數(shù)據(jù)處理、提供友好的用戶界面等重要的工作。
hCommWatchThread=
CreateThread( (LPSECURITY_ATTRIBUTES) NULL, //安全屬性
0,//初始化線程棧的大小,缺省為與主線程大小相同
(LPTHREAD_START_ROUTINE)CommWatchProc, //線程的全局函數(shù)
GetSafeHwnd(), //此處傳入了主框架的句柄
0, dwThreadID );
ASSERT(hCommWatchThread!=NULL);
⑷為輔助線程寫一個全局函數(shù),主要完成數(shù)據(jù)接收的工作。請注意OVERLAPPED結(jié)構(gòu)的使用,以及怎樣實現(xiàn)了非阻塞通信。
UINT CommWatchProc(HWND hSendWnd){
DWORD dwEvtMask=0 ;
SetCommMask( hCom, EV_RXCHAR|EV_TXEMPTY );//有哪些串口事件需要監(jiān)視?
WaitCommEvent( hCom, dwEvtMask, os );// 等待串口通信事件的發(fā)生
檢測返回的dwEvtMask,知道發(fā)生了什么串口事件:
if ((dwEvtMask EV_RXCHAR) == EV_RXCHAR){ // 緩沖區(qū)中有數(shù)據(jù)到達(dá)
COMSTAT ComStat ; DWORD dwLength;
ClearCommError(hCom, dwErrorFlags, ComStat ) ;
dwLength = ComStat.cbInQue ; //輸入緩沖區(qū)有多少數(shù)據(jù)?
if (dwLength 0) { BOOL fReadStat ;
fReadStat = ReadFile( hCom, lpBuffer,dwLength, dwBytesRead,READ_OS( npTTYInfo ) ); //讀數(shù)據(jù)
注:我們在CreareFile()時使用了FILE_FLAG_OVERLAPPED,現(xiàn)在ReadFile()也必須使用
LPOVERLAPPED結(jié)構(gòu).否則,函數(shù)會不正確地報告讀操作已完成了.
使用LPOVERLAPPED結(jié)構(gòu), ReadFile()立即返回,不必等待讀操作完成,實現(xiàn)非阻塞
通信.此時, ReadFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
if (!fReadStat){
if (GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom,READ_OS( npTTYInfo ), dwBytesRead, TRUE )){
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE) continue;//緩沖區(qū)數(shù)據(jù)沒有讀完,繼續(xù)
…… ……
::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主線程,串口收到數(shù)據(jù)}
所謂的非阻塞通信,也即異步通信。是指在進(jìn)行需要花費大量時間的數(shù)據(jù)讀寫操作(不僅僅是指串行通信操作)時,一旦調(diào)用ReadFile()、WriteFile(), 就能立即返回,而讓實際的讀寫操作在后臺運(yùn)行;相反,如使用阻塞通信,則必須在讀或?qū)懖僮魅客瓿珊蟛拍芊祷?。由于操作可能需要任意長的時間才能完成,于是問題就出現(xiàn)了。
非常阻塞操作還允許讀、寫操作能同時進(jìn)行(即重疊操作?),在實際工作中非常有用。
要使用非阻塞通信,首先在CreateFile()時必須使用FILE_FLAG_OVERLAPPED;然后在 ReadFile()時lpOverlapped參數(shù)一定不能為NULL,接著檢查函數(shù)調(diào)用的返回值,調(diào)用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后調(diào)用GetOverlappedResult()返回重疊操作(overlapped operation)的結(jié)果;WriteFile()的使用類似。
⑸.在主線程中發(fā)送下行命令。
BOOL fWriteStat ; char szBuffer[count];
…………//準(zhǔn)備好發(fā)送的數(shù)據(jù),放在szBuffer[]中
fWriteStat = WriteFile(hCom, szBuffer, dwBytesToWrite,
dwBytesWritten, WRITE_OS( npTTYInfo ) ); //寫數(shù)據(jù)
//我在CreareFile()時使用了FILE_FLAG_OVERLAPPED,現(xiàn)在WriteFile()也必須使用LPOVERLAPPED結(jié)構(gòu).否則,函數(shù)會不正確地報告寫操作已完成了.
使用LPOVERLAPPED結(jié)構(gòu),WriteFile()立即返回,不必等待寫操作完成,實現(xiàn)非阻塞 通信.此時, WriteFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
int err=GetLastError();
if (!fWriteStat) {
if(GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom, WRITE_OS( npTTYInfo ),
dwBytesWritten, TRUE )) {
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE){// normal result if not finished
dwBytesSent += dwBytesWritten; continue; }
......................
//我使用了多線程技術(shù),在輔助線程中監(jiān)視串口,有數(shù)據(jù)到達(dá)時依靠事件驅(qū)動,讀入數(shù)據(jù)并向主線程報告(發(fā)送數(shù)據(jù)在主線程中,相對說來,下行命令的數(shù)據(jù)總是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技術(shù),依靠重疊(overlapped)讀寫操作,讓串口讀寫操作在后臺運(yùn)行。
使用 NET Framework Microsoft Visual Basic開發(fā)人員可以創(chuàng)建健壯的 在先前的Visual Basic版本中很難編寫的應(yīng)用程序 本文將討論使用 NET Framework的好處 并且將包括一些功能強(qiáng)大的 Visual Basic開發(fā)人員可以與該框架一起使用的特性 包括多線程和線程池(thread pooling) Windows服務(wù)和文件系統(tǒng)監(jiān)控等 為什么使用框架 單詞框架(framework)有幾種含意 在這種情況中 它指的是創(chuàng)建和運(yùn)行應(yīng)用程序的基礎(chǔ) 擁有這樣的基礎(chǔ)使得創(chuàng)建應(yīng)用程序變得更容易 而同時使用了一個一致的 簡化的程序設(shè)計模型 作為一個Visual Basic 開發(fā)人員 你對于這種程序設(shè)計語言感覺很滿意 它使得創(chuàng)建各種應(yīng)用程序變得很容易 Visual Basic語言本身提供了固有的數(shù)據(jù)類型 如 Integer Long和String 以及一些最常用的函數(shù) 如字符串處理和數(shù)據(jù)類型轉(zhuǎn)換等 當(dāng)你的應(yīng)用程序變得更復(fù)雜時 你可以使用Win API來完成標(biāo)準(zhǔn)的Visual Basic函數(shù)所不能實現(xiàn)的功能 如獲取任意的注冊鍵和數(shù)值 在許多情況中 你還可以使用(Component Object Model 組件對象模型)組件庫來擴(kuò)展應(yīng)用程序的功能 最明顯的例子是ADO(ActiveX Data Objects)庫 你的應(yīng)用程序可以使用它來進(jìn)行數(shù)據(jù)訪問 雖然Visual Basic足夠靈活 可以提供這些不同的可擴(kuò)展性機(jī)制 但這種靈活性仍然需要你學(xué)習(xí)幾種復(fù)雜的API體系結(jié)構(gòu) 你需要了解Win 如何工作 以及如何在Visual Basic中調(diào)用它們 這可能會是一個既費時又容易出錯的任務(wù) 你還需要了解如何在Visual Basic中使用各種組件 每個組件都有一個不同的對象模型 最后 當(dāng)你使用Win API ADO 也可能使用許多其他組件 創(chuàng)建自己的Visual Basic應(yīng)用程序時 你需要管理這些應(yīng)用程序的部署以及它們的相關(guān)性 一個典型的Visual Basic應(yīng)用程序的相關(guān)性列表所包括的遠(yuǎn)遠(yuǎn)多于Visual Basic運(yùn)行時(runtime);它必須包括應(yīng)用程序使用的所有對象庫 如ADO 公共框架背后的想法是解決這些問題 并使得用戶創(chuàng)建健壯的應(yīng)用程序變得更容易 而無需學(xué)習(xí)多種不同的API體系結(jié)構(gòu) 并且無需部署和處理多種對象庫的版本問題 什么是 NET Framework 術(shù)語 NET Framework指的是構(gòu)成Microsoft NET平臺開發(fā)基礎(chǔ)的一組技術(shù) 這一組中的關(guān)鍵技術(shù)是運(yùn)行時(runtime)和類庫 運(yùn)行時負(fù)責(zé)管理代碼 在執(zhí)行時向它提供服務(wù) 這與Visual Basic 運(yùn)行時的作用類似 NET程序設(shè)計語言 包括Visual Basic NET Microsoft Visual C# C++管理的擴(kuò)展 以及多種來自不同開發(fā)商的程序設(shè)計語言 通過一組公共的統(tǒng)一類來利用各種服務(wù)和特性 NET統(tǒng)一類提供了創(chuàng)建應(yīng)用程序的基礎(chǔ) 而不管你使用何種語言 無論你只是簡單地連接一個字符串 還是創(chuàng)建一個Windows服務(wù)或多層的基于網(wǎng)絡(luò)的應(yīng)用程序 你都要用到這些統(tǒng)一類 統(tǒng)一類為訪問平臺的功能性提供了一種一致的方法 一旦你學(xué)會了使用類庫 你就會發(fā)現(xiàn)所有任務(wù)都遵循同一個一致的體系結(jié)構(gòu) 要編寫自己的應(yīng)用程序 你無需學(xué)習(xí)和掌握不同的API體系結(jié)構(gòu) 由于 NET Framework 部署Visual Basic NET應(yīng)用程序變得更容易了 與Visual Basic 應(yīng)用程序不同 你無需配置各種相關(guān)性 如單獨的數(shù)據(jù)訪問庫 XML語法分析器和網(wǎng)絡(luò)API 因為所有這些功能都是 NET Framework的組成部分 通過在統(tǒng)一的 集成的框架上創(chuàng)建自己的應(yīng)用程序 你可以實現(xiàn)學(xué)習(xí)這種框架所花費時間的最大回報 并且你將擁有更多容易部署和使用的健壯的應(yīng)用程序 NET Framework與Visual Basic NET Visual Basic 運(yùn)行時在簡化許多公共的程序設(shè)計任務(wù)方面非常重要 但是簡化這一層意味著 在擁有Visual Basic可以使用的打包程序之前 你不能使用新的操作系統(tǒng)特性 如DirectX 作為一個Visual Basic開發(fā)人員 你從 NET Framework獲得的最重要的益處是 可以使用一致的程序設(shè)計模型既直接又容易地訪問 NET平臺 這意味著 你可以使用Visual Basic NET創(chuàng)建很難或不可能使用Visual Basic 創(chuàng)建的應(yīng)用程序 作為一個Visual Basic 開發(fā)人員 現(xiàn)在你將對能夠使用與其他平臺語言相同的特性和功能而贊賞不已 例如 為了創(chuàng)建Windows服務(wù) 你無須再用Microsoft Visual C++來編寫它 你也無須求助于黑客或組裝機(jī) 你可以優(yōu)雅 干凈 容易地使用Visual Basic NET完成這項工作 為了給你一些使用 NET Framwork的例子 我們將討論在你的應(yīng)用程序中可能需要執(zhí)行的 個常見任務(wù) 跟蹤與事件記錄 多線程 文件系統(tǒng)監(jiān)控和創(chuàng)建Windows服務(wù) 跟蹤與事件記錄 當(dāng)創(chuàng)建一個健壯的應(yīng)用程序的時候 你必須密切注意診斷和故障排除機(jī)制 代表性地 這包括編寫處理打開輸出目標(biāo)(事件記錄或文件)的跟蹤組件 編寫跟蹤消息和關(guān)閉輸出目標(biāo) 然后通過自己的代碼調(diào)用關(guān)于這個組件的方法 將文本傳遞給記錄 你將所有的時間和精力花在了創(chuàng)建跟蹤和記錄子系統(tǒng)上 這最終并不會對解決商務(wù)問題有所貢獻(xiàn) 但這是創(chuàng)建應(yīng)用程序所必需的 NET Framework包括類和其他數(shù)據(jù)類型 通過向你提供記錄基礎(chǔ)設(shè)施 使得記錄跟蹤消息變得很容易 圖 給出了用于跟蹤的 NET Framework類 類是System Diagnostics名稱空間的一部分 Trace類提供了幾個共享的方法 例如 Write方法允許你記錄特定消息 而Assert方法允許你在特定的條件為假的情況下記錄一條消息 Trace類將消息輸出到Listeners集合中的對象 這個集合中的每個對象都屬于繼承自TraceListener的一個類 EventLogTraceListener 將消息寫入事件記錄 而TextWriterTraceListener則是將消息寫入到一個文本文件中 默認(rèn)情況下 DefaultTraceListener的一個實例被添加到Trace類的Listeners集合中 除了標(biāo)準(zhǔn)的監(jiān)聽程序以外 你可以實施自己跟蹤監(jiān)聽程序 例如 你希望接收來自在防火墻后面的遠(yuǎn)程機(jī)器上運(yùn)行的應(yīng)用程序的跟蹤輸出 你可以編寫一個跟蹤監(jiān)聽程序 通過HTTP全球向你的服務(wù)器發(fā)送跟蹤消息 這將會影響你的應(yīng)用程序的性能 但只會在啟用跟蹤時才會對性能有所影響 代表性地 你需要有能力在編譯的二進(jìn)制文件中包括或去除跟蹤代碼 為了在Visual Basic 中做到這一點 你需要使用編譯常量 并在#If語句中包含所有的跟蹤代碼 這使得代碼很難理解和維護(hù) 利用 NET Framework 你只需在項目屬性(Project Properties)對話框中將TRACE編譯常量設(shè)為on或off狀態(tài) 你無需在#If語句中包括跟蹤代碼 另一個普遍期望的跟蹤特性是跟蹤水平設(shè)置 這包括不同的跟蹤設(shè)置 例如 Severe(嚴(yán)重) Error(錯誤) Warning(警告)和Information(信息) 這些設(shè)置對記錄哪些信息進(jìn)行控制 你可以使用跟蹤組件啟動時所讀取的注冊表數(shù)值對此進(jìn)行控制 對于 NET Framework 這是完全內(nèi)置的功能 你可以設(shè)置一個注冊表數(shù)值來控制你當(dāng)前的應(yīng)用程序的記錄水平 比如 只有在跟蹤水平被設(shè)置為Severe(嚴(yán)重)的情況下 才使用Trace WriteIf和Trace WriteLineIf來記錄消息 集成的跟蹤和記錄特性極大地增強(qiáng)了生產(chǎn)力 因為你只需使用內(nèi)置的特性 將精力集中在編 *** 正的應(yīng)用程序代碼上
多線程應(yīng)用程序 NET Framework的一個很重要的特性是 可以在不使用第三方工具或不支持的Visual Basic技巧情況下 使用Visual Basic創(chuàng)建多線程應(yīng)用程序 NET Framework的多線程支持是由System Threading名稱空間中的類和接口提供的 因此所有的 NET語言都能夠以相同的方式創(chuàng)建和處理線程 System Threading Thread是一個核心類 提供了對創(chuàng)建和控制線程的支持 要創(chuàng)建一個線程 你可以創(chuàng)建一個新的System Threading Thread對象 將構(gòu)造函數(shù)傳遞給ThreadStart代理 這個代理提供了這個線程開始執(zhí)行的方法 當(dāng)你準(zhǔn)備啟動這個新的線程時 可以調(diào)用Thread Start() (請參閱清單 ) 當(dāng)你開始創(chuàng)建多線程應(yīng)用程序時 你很快就會認(rèn)識到需要控制對共享資源的訪問 如共享的類變量 NET Framework還包括幾個類和數(shù)據(jù)類型 你可以使用它們對兩個線程執(zhí)行的動作進(jìn)行同步 在最簡單的情況中 你由一個需要從不同的線程中進(jìn)行更新的共享變量 要這樣做 你可以使用System Threading Interlocked類 例如 你可以通過編寫Interlocked Increment(num)或Interlocked Decrement(num)分別使名為num的共享變量遞增或遞減 你還可以使用Interlocked將變量設(shè)為某一特定值 或檢查兩個變量是否相等 除了這種簡單情況以外 你可以使用 NET Framework類來執(zhí)行更復(fù)雜的線程同步 如事件和互斥體的同步 所有都來自于 NET Framework內(nèi)部 而無須使用Win API Imports System IO注釋 The namespace System Threading注釋 contains the Thread classImports System ThreadingModule Module Private count As LongSub Main()注釋 Create the ThreadStart delegateDim tStart As ThreadStart = New _ ThreadStart(AddressOf StartCounting)注釋 Create the threadDim t As Thread = New Thread(tStart)Console WriteLine( Enter q to quit )t Start() 注釋 start the threadWhile (Console Read() asc( q ))注釋 repeat the loop until the user enters qEnd Whilet Stop() 注釋 tell thread to stop processingt Join() 注釋 wait until the thread finishesEnd SubSub StartCounting()Do注釋 use Interlocked Increment in case 注釋 another thread is accessing the same variableInterlocked Increment(count)Console WriteLine( _ After incrementing count is : count)Thread Sleep( )LoopEnd SubEnd Module 清單 使用Visual Basic NET創(chuàng)建線程 你創(chuàng)建了一個新線程 將它傳遞給一個ThreadStart代理 然后調(diào)用Thread Start()啟動這個線程 你可以通過調(diào)用Thread Stop()來中止這個線程 然后調(diào)用Thread Join()等待它完成關(guān)閉操作 一個線程可以使用System Threading Interlocked來使變量遞增或遞減 此外 NET Framework提供了一個方便的機(jī)制來對工作排隊 并將起分配給線程池中的某個線程 在處理多個并發(fā)工作項目或工作請求的服務(wù)器應(yīng)用程序中 這非常有用 例如 對于等待輸入文件 然后將它們導(dǎo)入到數(shù)據(jù)庫中去的應(yīng)用程序 可能會對每個輸入文件進(jìn)行排隊 以在線程池中的某個單獨的線程上進(jìn)行處理 System Threading ThreadPool類允許你使用共享的QueueUserWorkItem方法對工作進(jìn)行排隊 以前要這樣做 你必須得創(chuàng)建和管理自己的線程池 你又需要在基礎(chǔ)設(shè)施工作而不是在解決商務(wù)問題上花大量的時間和精力 文件系統(tǒng)監(jiān)控 我曾經(jīng)遇到過一些應(yīng)用程序 需要等待和處理某個特定目錄中的文件 例如 將數(shù)據(jù)從文件導(dǎo)入到數(shù)據(jù)庫中去的應(yīng)用程序 數(shù)據(jù)文件可以從某個大型機(jī)上下載 或者被轉(zhuǎn)移到某個輸入目錄中 該應(yīng)用程序?qū)⑺鼈儗?dǎo)入到數(shù)據(jù)庫中 你不用經(jīng)常地輪詢該目錄檢查是否有新文件 可以等待生成新文件的通知 你可以在Visual Basic 中使用Win API來做到這一點 而在Visual Basic NET中你可以使用 NET Framework類來做這項工作 但是在 NET中實施文件監(jiān)控與在 NET中完成其他工作的方法更加一致 因此學(xué)習(xí)曲線是最小的 你可以使用System IO FileSystemWatcher NET類對文件系統(tǒng)進(jìn)行監(jiān)視 它提供了一些屬性 允許你設(shè)置監(jiān)控的路徑 指定是對文件還是子目錄層次的變化感興趣 System IO FileSystemWatcher還允許你指定需要監(jiān)控的文件名和文件類型(例如 * xml是指監(jiān)控所有XML文件的變化) 最后 你可以指定感興趣的變化類型 例如 只對新建文件 文件屬性的變化或文件大小的變化(請參閱清單 )感興趣 在你設(shè)置了監(jiān)控內(nèi)容后 你需要鉤住用于感興趣的各種事件的事件處理程序 FileSystemWatcher事件有Changed Created Deleted Error和Renamed 要處理某個事件 首先你需要編寫一個與FileSystemEventHandler代理相同聲明的事件處理程序 然后將這個處理程序添加到FileSystemWatcher類中 這個基于代理的體系結(jié)構(gòu)允許你為同一個事件添加多個處理程序 或者對于多個事件使用同一個處理程序 而你不能使用Visual Basic 做到這一點 注釋 System IO contains the 注釋 file monitoring classes and typesImports System IOModule Module Sub Main() 注釋 FileSystemWatcher does the real work Dim fw As New FileSystemWatcher() 注釋 WaitForChangedResult is what you 注釋 get back when a change occurs Dim result As WaitForChangedResult 注釋 set the path to monitor fw Path = C:WINNT 注釋 tell it whether to watch files or directories fw Target = WatcherTarget File 注釋 tell it whether to include subdirs fw IncludeSubdirectories = False 注釋 hook up handlers AddHandler fw Created New FileSystemEventHandler(AddressOf OnFileNotify) 注釋 enable the watcher fw Enabled = True DoConsole WriteLine( Beginning to monitor ) 注釋 this is where we actually wait注釋 waiting blocks execution for the specified timeoutresult = fw WaitForChanged(WatcherChangeTypes All )Console WriteLine( Hit Enter to continue q to quit ) Loop While (Console ReadLine q )End Sub注釋 This is the delegate that gets 注釋 called when a file is created Public Sub OnFileNotify(ByVal source As Object ByVal e As FileSystemEventArgs)Console WriteLine( Notification received for file change type is _e FullPath e ChangeType) End SubEnd Module 清單 使用FileSystemWatcher監(jiān)控某個文件夾是否有新文件
lishixinzhi/Article/program/net/201311/11618
1.Socket + Threads/ThreadPool大概性能:小于1500個連接 實現(xiàn):Accept一個Socket,就交給一個線程去管理,比較笨,但也比較有效,因為是同步方式,控制起來很方便。高級點的,就是交給一個線程池去管理,線程池由系統(tǒng)自動托管,省去了開銷線程的時間。一般小型項目,用這個完全足夠,開發(fā)也簡單。但要注意,如果若干Socket長時間占用線程池中的線程,同時其它連接數(shù)又比較多,很容易出現(xiàn)提示說你沒有足夠的線程供使用。呵呵,讓Socket少做點事,少占用時間,換一個快點的CPU是不錯的方式。另外,如果有一些比較好的第三方線程池組件,也可以選擇使用,比如SmartThreadPool。2.Socket + Select大概性能:大于1500個連接后性能下降 實現(xiàn):Select是很常用的一種模型。是在阻塞功能中輪詢一個或多個Socket,將要處理的Socket放到一個IList中,當(dāng)Select輪詢結(jié)束后,然后我們再自己處理這個IList中的Socket。具體的用法可以看一下MSDN。Select的效率并不能說是高的,因為當(dāng)隊列中待處理的Socket比較多的時候,處理最后幾個Socket相當(dāng)于要遍歷所有前面的Socket,非常不劃算的.3.Socket + Asynchronous大概性能:約7500個客戶端連接 實現(xiàn):BeginXXXX,EndXXXX,再熟悉不過了吧。異步Socket歸根到底,還是用的線程池技術(shù),用線程池來處理異步IO。這就又引出個問題,.NET的線程池又是用的什么實現(xiàn)方式,以前看過有人說,.NET的線程池是用的完成端口來實現(xiàn)的,我不知道這樣的說法是不是正確,從查到的資料中也沒有辦法確認(rèn)(希望這點有朋友可以告訴我)。異步Socket對于程序的處理流程來說比同步復(fù)雜了許多,異步回調(diào)函數(shù)的控制不如同步方式那樣直觀。但有一點我想應(yīng)該是要注意的,就是回調(diào)函數(shù)應(yīng)該輕裝上陣,不應(yīng)該處理過多的事務(wù),對傳遞數(shù)據(jù)的處理,應(yīng)該交給其它線程進(jìn)行處理。 4.IOCP(完成端口)大概性能:約20000~50000個客戶端連接 實現(xiàn):現(xiàn)在.NET下有一些偽IOCP,大家可以去搜索一下,還沒有見過開放出來的用這些偽IOCP來實現(xiàn)的SOCKET例子。我說的20000~50000個客戶端連接,是指在C++下開發(fā)的情況,這樣的情況下,需要用到的基本技術(shù)還包括內(nèi)存池、查詢算法等。偽IOCP能實現(xiàn)多少最大連接,沒有資料可以查,如果有朋友知道,可以討論一下。另外上 面提到的許多數(shù)據(jù),是從一些資料上摘抄下來的,我沒有自己試過,僅僅是拿出來和大家討論一下。1. Introduction - Native Win32 IOCPI/O Completion Ports (IOCP) supported on Microsoft Windows platforms has two facets. It first allows I/O handles like file handles, socket handles, etc., to be associated with a completion port. Any async I/O completion event related to the I/O handle associated with the IOCP will get queued onto this completion port. This allows threads to wait on the IOCP for any completion events. The second facet is that we can create a I/O completion port that is not associated with any I/O handle. In this case, the IOCP is purely used as a mechanism for efficiently providing a thread-safe waitable queue technique. This technique is interesting and efficient. Using this technique, a pool of a few threads can achieve good scalability and performance for an application. Here is a small example. For instance, if you are implementing a HTTP server application, then you need to do the following mundane tasks apart from the protocol implementation:Create a client connection listen socket. Once we get the client connection, use the client socket to communicate with the client to and fro. You can implement it by creating one dedicated thread per client connection that can continuously communicate with the client to and fro. But this technique quickly becomes a tremendous overhead on the system, and will reduce the performance of the system as the number of simultaneous active client connections increase. This is because, threads are costly resources, and thread switching is the major performance bottle neck especially when there are more number of threads.The best way to solve this is to use an IOCP with a pool of threads that can work with multiple client connections simultaneously. This can be achieved using some simple steps...Create a client connection listen socket. Once we get the client connection, post an IOCP read message on the socket to an IOCP. One of the threads waiting for completion events on this IOCP will receive the first read message for the client. It immediately posts another read onto the same IOCP and continues processing the read message it got. Once processing the read message is completed, it again waits on the IOCP for another event. This technique will allow a small pool of threads to efficiently handle communication with hundreds of client connections simultaneously. Moreover, this is a proven technique for developing scalable server side applications on Windows platforms.The above is a simplified description of using IOCP in multithreaded systems. There are some good in-depth articles on this topic in CodeProject and the Internet. Do a bit of Googling on words like IO Completion Ports, IOCP, etc., and you will be able to find good articles.2. Introduction - Managed IOCPManaged IOCP is a small .NET class library that provides the second facet of Native Win32 IOCP. This class library can be used both by C# and VB.NET applications. I chose the name Managed IOCP to keep the readers more close to the techniques they are used to with native Win32 IOCP. As the name highlights, Managed IOCP is implemented using pure .NET managed classes and pure .NET synchronization primitives. At its core, it provides a thread-safe object queuing and waitable object receive mechanism. Apart from that, it provides a lot more features. Here is what it does:Multiple Managed IOCP instances per process. Registration of multiple threads per Managed IOCP instance. Dispatching System.Object types to a threadsafe queue maintained by each Managed IOCP instance. Waitable multi-thread safe retrieval of objects from the Managed IOCP instance queue by all the threads registered for that particular Managed IOCP instance. Ability to restrict the number of concurrent active threads processing the queued objects related to a particular Managed IOCP instance. Policy based replaceable/customizable approach for choosing a registered thread to process the next available queued object. Ability to pause the Managed IOCP processing. Internally, pauses processing of queued objects by registered threads. Also, by default, disallows enqueuing new objects (can be changed). Run the Managed IOCP instance. Internally re-starts the processing of queued objects by registered threads. Also allows enqueuing new objects (if it is disallowed previously). Modify the max. allowed concurrent threads at runtime. Provides easy accessibility to Managed IOCP instance runtime properties like... Number of active concurrent threads. Number of objects left in queue. Number of allowed concurrent threads. Running status. Safe and controlled closing of a Managed IOCP instance. 2.1. Managed IOCP in Job/Task Oriented Business ProcessesManaged IOCP can be used in other scenarios apart from the sample that I mentioned in the introduction to native Win32 IOCP. It can be used in process oriented server side business applications. For instance, if you have a business process ( _not_ a Win32 process) with a sequence of tasks that will be executed by several clients, you will have to execute several instances of the business process, one for each client in parallel. As mentioned in my introduction to native Win32 IOCP, you can achieve this by spawning one dedicated thread per business process instance. But the system will quickly run out of resources, and the system/application performance will come down as more instances are created. Using Managed IOCP, you can achieve the same sequential execution of multiple business process instances, but with fewer threads. This can be done by dispatching each task in a business process instance as an object to Managed IOCP. It will be picked up by one of the waiting threads and will be executed. After completing the execution, the thread will dispatch the next task in the business process instance to the same Managed IOCP, which will be picked up by another waiting thread. This is a continuous cycle. The advantage is that you will be able to achieve the sequential execution goal of a business process, as only one waiting thread can receive a dispatched object, and at the same time keep the system resource utilization to required levels. Also, the system and business process execution performance will increase as there are few threads executing multiple parallel business processes.3. Using Managed IOCP in .NET applicationsMultithreaded systems are complex in the context that most problems will show up in real time production scenarios. To limit the possibility of such surprises while using Managed IOCP, I created a test application using which several aspects of the Managed IOCP library can be tested. Nevertheless, I look forward for any suggestions/corrections/inputs to improve this library and its demo application.Before getting into the demo application, below is the sequence of steps that an application would typically perform while using the Managed IOCP library:Create an instance of the ManagedIOCP class: 2. using Sonic.Net;ManagedIOCP mIOCP = new ManagedIOCP();The ManagedIOCP constructor takes one argument, concurrentThreads. This is an integer that specifies how many maximum concurrent active threads are allowed to process objects queued onto this instance of ManagedIOCP. I used a no argument constructor, which defaults to a maximum of one concurrent active thread.From a thread that needs to wait on objects queued onto the ManagedIOCP instance, call the Register() method on the ManagedIOCP instance. This will return an instance of the IOCPHandle class. This is like native Win32 IOCP handle, using which the registered thread can wait on the arrival of objects onto the ManagedIOCP instance. This thread can use the Wait() method on the IOCPHandle object. The Wait() will indefinitely wait until it grabs an object queued onto the ManagedIOCP instance to which the calling thread is registered. It either comes out with an object, or an exception in case the ManagedIOCP instance is stopped (we will cover this later). 4. IOCPHandle hIOCP = mIOCP.Register();5. while(true)6. {7. try8. {9. object obj = hIOCP.Wait();10. // Process the object11. 12. }13. catch(ManagedIOCPException e)14. {15. break;16. }17. catch(Exception e)18. {19. break;20. }}Any thread (one that is registered with the ManagedIOCP instance and any non-registered thread) that has access to the ManagedIOCP instance can dispatch (Enqueue) objects to it. These objects are picked up by waiting threads that are registered with the ManagedIOCP instance onto which objects are being dispatched. 22. string str = "Test string";mIOCP.Dispatch(str);When a thread decides not to wait for objects any more, it should un-register with the ManagedIOCP instance. mIOCP.UnRegister();Once the application is done with an instance of ManagedIOCP, it should call the Close() method on it. This will release any threads waiting on this instance of ManagedIOCP, clears internal resources, and resets the internal data members, thus providing a controlled and safe closure of a ManagedIOCP instance. mIOCP.Close();There are certain useful statistics that are exposed as properties in the ManagedIOCP class. You can use them for fine tuning the application during runtime.// Current number of threads that are // concurrently processing the objects queued // onto this instance of Managed IOCP // (This is readonly property) int activeThreads = mIOCP.ActiveThreads;// Max number of concurrent threads // allowed to process objects queued onto this // instance of Managed IOCP (This is a read/write property) int concurThreads = mIOCP.ConcurrentThreads;// Current count of objects queued onto this Managed IOCP instance. // NOTE: This value may change very quickly // as multiple concurrent threads might // be processing objects from this instance of Managed IOCP queue. // So _do not_ depend on this value // for logical operations. Use this only for // monitoring purpose (Status reporting, etc.) // and during cleanup processes // (like not exiting main thread untill the queued object becomes 0, // i.e. no more objects to be processed, etc) // (This is readonly property) int qCount = mIOCP.QueuedObjectCount;// Number of threads that are registered with this instance of Managed IOCP // (This is readonly property) int regThreadCount = mIOCP.RegisteredThreads;3.1. Advanced usageFollowing are the advanced features of Managed IOCP that need to be used carefully.Managed IOCP execution can be paused at runtime. When a Managed IOCP instance is paused, all the threads registered with this instance of Managed IOCP will stop processing the queued objects. Also, if the 'EnqueueOnPause' property of the ManagedIOCP instance is false (by default, it is false), then no thread will be able to dispatch new objects onto the Managed IOCP instance queue. Calling Dispatch on the ManagedIOCP instance will throw an exception in the Pause state. If the 'EnqueueOnPause' property is set to true, then threads can dispatch objects onto the queue, but you need to be careful while setting this property to true, as this will increase the number of pending objects in the queue, thus occupying more memory. Also, when the Managed IOCP instance is re-started, all the registered threads will suddenly start processing a huge number of objects thus creating greater hikes in the system resource utilization.mIOCP.Pause();Once paused, the ManagedIOCP instance can be re-started using the Run method.mIOCP.Run();The running status of the Managed IOCP instance can be obtained using the IsRunning property:bool bIsRunning = mIOCP.IsRunning;You can retrieve the System.Threading.Thread object of the thread associated with the IOCPHandle instance, from its property named 'OwningThread'.3.2. Demo ApplicationI provided two demo applications with similar logic. The first is implemented using Managed IOCP, the other using native Win32 IOCP. These two demo applications perform the following steps:Create a global static ManagedIOCP instance or native Win32 IOCP. Create five threads. Each thread will dispatch one integer value at a time to the ManagedIOCP instance or native Win32 IOCP until the specified number of objects are completed. Start (creates a new set of five threads) and stop (closes the running threads) the object processing. The Sonic.Net (ManagedIOCP) demo application additionally demonstrates the following features of Managed IOCP that are unavailable in the Win32 IOCP:Pause and continue object processing during runtime. Change concurrent threads at runtime. Statistics like, Active Threads, Maximum Concurrent threads, Queued Objects Count and Running Status of Managed IOCP. Below is the image showing both the demo applications after their first cycle of object processing:Demo application resultsAs you can see in the above figure, Managed IOCP gives the same speed (slightly even better) as native Win32 IOCP. The goal of these two demo applications is _not_ to compare the speed or features of Win32 IOCP with that of Managed IOCP, but rather to highlight that Managed IOCP provides all the advantages of native Win32 IOCP (with additional features) but in a purely managed environment.I tested these two demo applications on a single processor CPU and a dual processor CPU. The results are almost similar, in the sense the Managed IOCP is performing as good as (sometimes performing better than) native Win32 IOCP.3.3. Source and demo application filesBelow are the details of the files included in the article's Zip file:Sonic.Net (folder) - I named this class library as Sonic.Net (Sonic stands for speed). The namespace is also specified as Sonic.Net. All the classes that I described in this article are defined within this namespace. The folder hierarchy is described below: 2. Sonic.Net3. |4. -- Assemblies5. |6. -- Solution Files7. |8. -- Sonic.Net9. |10. -- Sonic.Net Console Demo 11. |-- Sonic.Net Demo ApplicationThe Assemblies folder contains the Sonic.Net.dll (contains the ObjectPool, Queue, ManagedIOCP, IOCPHandle, and ThreadPool classes), Sonic.Net Demo Application.exe (demo application showing the usage of ManagedIOCP and IOCPHandle classes), and Sonic.Net Console Demo.exe (console demo application showing the usage of ThreadPool and ObjectPool classes).The Solution Files folder contains the VS.NET 2003 solution file for the Sonic.Net assembly project, Sonic.Net demo application WinForms project, and Sonic.Net console demo project.The Sonic.Net folder contains the Sonic.Net assembly source code.The Sonic.Net Console Demo folder contains the Sonic.Net console demo application source code. This demo shows the usage of the Managed IOCP ThreadPool, which is explained in my Managed I/O Completion Ports - Part 2 article. This demo uses a file that will be read by the ThreadPool threads. Please change the file path to a valid one on your system. The code below shows the portion in the code to change. This code is in the ManagedIOCPConsoleDemo.cs file.public static void ReadData(){ StreamReader sr = File.OpenText(@"C:\aditya\downloads\lgslides.pdf"); string st = sr.ReadToEnd(); st = null; sr.Close(); Thread.Sleep(100);}The Sonic.Net Demo Application folder contains the Sonic.Net demo application source code.Win32IOCPDemo (folder) - This folder contains the WinForms based demo application for demonstrating Win32 IOCP usage using PInvoke. When compiled, the Win32IOCPDemo.exe will be created in the Win32IOCPDemo\bin\debug or Win32IOCPDemo\bin\Release folder based on the current build configuration you selected. The default build configuration is set to Release mode. 4. Inside Managed IOCPThis section discusses the how and why part of the core logic that is used to implement Managed IOCP.4.1. Waiting and retrieving objects in Managed IOCPManaged IOCP provides a thread safe object dispatch and retrieval mechanism. This could have been achieved by a simple synchronized queue. But with synchronized queue, when a thread (thread-A) dispatches (enqueues) an object onto the queue, for another thread (thread-B) to retrieve that object, it has to continuously monitor the queue. This technique is inefficient as thread-B will be continuously monitoring the queue for arrival of objects, irrespective of whether the objects are present in the queue. This leads to heavy CPU utilization and thread switching in the application when multiple threads are monitoring the same queue, thus degrading the performance of the system.Managed IOCP deals with this situation by attaching an auto reset event to each thread that wants to monitor the queue for objects and retrieve them. This is why any thread that wants to wait on a Managed IOCP queue and retrieve objects from it has to register with the Managed IOCP instance using its 'Register' method. The registered threads wait for the object arrival and retrieve them using the 'Wait' method of the IOCPHandle instance. The IOCPHandle instance contains an AutResetEvent that will be set by the Managed IOCP instance when any thread dispatches an object onto its queue. There is an interesting problem in this technique. Let us say that there are three threads, thread-A dispatching the objects, and thread-B and thread-C waiting on object arrival and retrieving them. Now, say if thread-A dispatches 10 objects in its slice of CPU time. Managed IOCP will set the AutoResetEvent of thread-B and thread-C, thus informing them of the new object arrival. Since it is an event, it does not have an indication of how many times it has been set. So if thread-B and thread-C just wake up on the event set and retrieve one object each from the queue and again waits on the event, there would be 8 more objects left over in the queue unattended. Also, this mechanism would waste the CPU slice given to thread-B and thread-C as they are trying to go into waiting mode after processing a single object from the Managed IOCP queue.So in Managed IOCP, when thread-B and thread-C call the 'Wait' method on their respective IOCPHandle instances, the method first tries to retrieve an object from the Managed IOCP instance queue before waiting on its event. If it was able to successfully retrieve the object, it does