關鍵詞 串行通訊 多線程 程序設計
在自動化工業控制應用中,經常需要計算機與外圍設備進行數據通訊。而異步串行通訊是一種常用的通訊手段。在單任務操作系統中,不能同時處理兩件以上不同的任務。Win32是基於線程的多任務操作系統,使得應用程序能同時執行多個任務,即在一個進程中可同時運行多個線程。利用Win32的這個特點,在通訊過程中可以適當降低數據丟失率,提高系統可靠性。
隨著Win95系統的逐步普及,程序員們更願意在Win95下編程。而Delphi也越來越為廣大程序員所喜愛。然而,令人遺憾的是在Delphi環境中沒有象其它的一些編程語言一樣提供標准通訊控件。因此,利用Delphi進行通訊程序設計時,不但要掌握多線程編程技術,還要了解一些與通訊相關的API函數的使用。
一 多線程基本概念
首先介紹進程概念。一個進程通常定義為程序的一個實例。在Win32中,進程占據4GB地址空間。實際上,一個進程可以包含幾個線程,它們可以同時執行進程的地址空間中的代碼。為了運行所有這些線程,操作系統以輪轉方式為每個獨立線程分配一些CPU時間片。這給人一種假象,好像這些線程是在同時運行。創建一個Win32進程時,它的第一個線程稱為主線程,由系統自動生成。然後可由主線程生成其它的線程,這些線程又可生成更多的線程。
線程描述了進程內的執行,是組成進程的基本單位。每次初始化一個進程時,系統創建一個主線程。通常對於許多應用程序,主線程是應用程序的唯一線程。但是,進程也可以創建額外的線程,目的在於盡可能充分合理的利用CPU時間。線程可以使用CreateThread()函數來創建。
在有若干線程並行運行的環境裡,同步各不同線程活動的能力是非常重要的,這樣可以避免對共享資源的訪問沖突。事件對象是同步線程的最基本形式,它用以向其它線程發信號以表示某一操作已經完成。例如,一個進程可能運行了兩個線程。第一個線程從文件讀數據到內存緩沖區中。每當數據已被讀入,第一個線程就發信號給第二個線程它可以處理數據了。當第二個線程完成了對數據的處理時,它可能需要再次給第一個線程發信號以讓第一個線程能夠從文件中讀入下一塊數據。事件可以使用CreateEvent()函數來創建。線程和事件在任何時候都處於兩種狀態之一:有信號和無信號。當線程被創建和正在運行時,它是無信號的。一旦線程終止,它就變成有信號的。線程可以通過使用SetEvent()和ResetEvent()函數來將事件置成有信號和無信號。
除了以上介紹的概念和函數,在通訊程序中還要用到等待函數WaitForSingleObject()和重疊I/O操作。等待函數能使線程阻塞自身執行,而重疊I/O操作能使費時的操作在後台中運行。
二 通訊程序設計
在Windows環境下,對於串行通訊的控制是通過中斷機制驅動的,由系統自行處理。Windows禁止應用程序直接和硬件打交道,程序員只能使用Windows提供的標准函數通過通訊驅動程序與硬件接口。首先,用CreateFile()函數打開通訊端口,然後通過SetupComm() 函數給通訊的輸入輸出隊列分配一定大小的內存緩沖區,接著通過BuildCommDCB()函數 和SetCommState()等函數對主要通訊參數進行設置。初始化完成後就可以利用ReadFile()函數和 WriteFile() 函數對通訊端口進行讀寫操作了。程序界面如圖所示。
本文提供的實例程序使用簡單方便。利用一條串行數據線連接在兩台計算機Com2之間就可以進行文本文件傳輸。對於Delphi的具體編程方法這裡不再贅述。實例中有詳細注釋。
unit comunate;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Buttons, StdCtrls, ComCtrls;
const
WM_COMMNOTIFY = WM_USER + 1; // 通訊消息
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
OpenDialog1: TOpenDialog;
Label1: TLabel;
BitBtn1: TBitBtn;
RichEdit1: TRichEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
procedure WMCOMMNOTIFY(var Message :TMessage);message WM_COMMNOTIFY;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
hNewCommFile,Post_Event: THandle;
Read_os : Toverlapped;
Receive :Boolean;
ReceiveData : DWord;
procedure AddToMemo(Str:PChar;Len:DWord); // 接收的數據送入顯示區
begin
str[Len]:=#0;
Form1.RichEdit1.Text:=Form1.RichEdit1.Text+StrPas(str);
end;
procedure CommWatch(Ptr:Pointer);stdcall; // 通訊監視線程
var
dwEvtMask,dwTranser : DWord;
Ok : Boolean;
Os : Toverlapped;
begin
Receive :=True;
FillChar(Os,SizeOf(Os),0);
Os.hEvent :=CreateEvent(nil,True,False,nil); // 創建重疊讀事件對象
if Os.hEvent=null then
begin
MessageBox(0,'Os.Event Create Error !','Notice',MB_OK);
Exit;
end;
if (not SetCommMask(hNewCommFile,EV_RXCHAR)) then
begin
MessageBox(0,'SetCommMask Error !','Notice',MB_OK);
Exit;
end;
while(Receive) do
begin
dwEvtMask:=0;
// 等待通訊事件發生
if not WaitCommEvent(hNewCommFile,dwEvtMask,@Os) then
begin
if ERROR_IO_PENDING=GetLastError then
GetOverLappedResult(hNewCommFile,Os,dwTranser,True)
end;
if ((dwEvtMask and EV_RXCHAR)=EV_RXCHAR) then
begin
// 等待允許傳遞WM_COMMNOTIFY通訊消息
WaitForSing
[1] [2] [3] 下一頁