原文:http://www.cnblogs.com/94YY/archive/2011/10/05/2199346.html
Delphi中使用TMultiReadExclusiveWriteSynchronizer類進行資料同步
(調試環境:Delphi2007+WinXPsp3 常式:Tst_Thread5.dpr)
前面的例子都是同類線程的不同執行個體來讀寫全域變數,用臨界區、互斥等來鎖住同段代碼。現在碰到的問題是,A,B兩個不同類型的線程,如何安全地來讀寫全域變數。
unit Tst_Thread5U;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,SyncObjs;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TFirstThread=Class(TThread)
protected
procedure Execute;override;
procedure ShowNum;
end;
TSecondThread=Class(TThread)
protected
procedure Execute;override;
procedure ShowNum;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
MaxSize=12;
var
WhichRun:integer;
GlobalArry:array[1..MaxSize] of Integer;
LockCS:TCriticalSection; //uses SyncObjs
LockMREWS:TMultiReadExclusiveWriteSynchronizer; //uses SysUtils
//===============
procedure AddNum;
var
i,j:integer;
begin
if WhichRun=2 then LockCS.Acquire;
if WhichRun=3 then LockMREWS.BeginRead;
j:=GlobalArry[MaxSize];
if WhichRun=3 then LockMREWS.EndRead;
if WhichRun=3 then LockMREWS.BeginWrite;
for i := 1 to MaxSize do
begin
GlobalArry[i]:=j;
inc(j);
Sleep(5);
end;
if WhichRun=2 then LockCS.Release;
if WhichRun=3 then LockMREWS.EndWrite;
end;
procedure TFirstThread.Execute;
begin
FreeOnTerminate:=True;
AddNum;
Synchronize(ShowNum);
end;
procedure TFirstThread.ShowNum;
var
i:integer;
begin
for i := 1 to MaxSize do
Form1.Memo1.Lines.Add(inttostr(GlobalArry[i]));
end;
procedure TSecondThread.Execute;
begin
FreeOnTerminate:=True;
AddNum;
Synchronize(ShowNum);
end;
procedure TSecondThread.ShowNum;
var
i:integer;
begin
for i := 1 to MaxSize do
Form1.Memo1.Lines.Add(inttostr(GlobalArry[i]));
end;
//未阻塞
procedure TForm1.Button1Click(Sender: TObject);
begin
WhichRun:=1;
TFirstThread.Create(False);
TSecondThread.Create(False);
end;
//阻塞 TCriticalSection
procedure TForm1.Button2Click(Sender: TObject);
begin
WhichRun:=2;
TFirstThread.Create(False);
TSecondThread.Create(False);
end;
//TMREWS
procedure TForm1.Button3Click(Sender: TObject);
begin
WhichRun:=3;
TFirstThread.Create(False);
TSecondThread.Create(False);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
LockCS:=TCriticalSection.Create;
LockMREWS:=TMultiReadExclusiveWriteSynchronizer.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
LockCS.Free;
LockMREWS.Free;
end;
end.
一、用TCriticalSection類來阻塞。(ps:這裡的咚咚都涉及Win32編程,我對這些不瞭解,先掌握如何運用吧。)這個有點類似臨界區。要Uses SyncObjs單元。
1、聲明一個全域的TCriticalSection類的執行個體。
2、建立TCriticalSection.Create,最好是在程式onCreate裡面,這樣才能保證對所有線程進行控制。
3、在全域變數訪問前用TCriticalSection.Acquire或TCriticalSection.Enter來阻塞。
4、訪問完畢,用TCriticalSection.Release或TCriticalSection.Leave來解除阻塞。
5、在恰當的位置進行TCriticalSection.Free,一般在程式的onDestroy裡。
二、用TMultiReadExclusiveWriteSynchronizer類來阻塞。可以寫成TMREWSync。這個類在SysUtils單元裡。(ps:天哪,那麼長的單詞我第一次見)。TMultiReadExclusiveWriteSynchronizer和TCriticalSection不同的是,它允許多個線程同時讀一個變數,只是在寫一個變數時才需要事先阻塞,因為只有多個線程同時寫一個變數才有可能造成衝突。
使用TMREWS的優勢是它允許多線程的並發讀取,同時又與CriticalSection一樣允許讀的時候只有一個線程訪問。劣勢是TMREWS用起來要費更高的代價。
1、聲明一個全域的TMultiReadExclusiveWriteSynchronizer類的執行個體。
2、建立TMultiReadExclusiveWriteSynchronizer.Create。
3、每個線程在讀一個全域變數前要先調用該類的BeginRead()來檢查是否有其他線程在寫這個變數。如果有,就等待,直到其他線程不再寫這個變數才返回。當讀好後,調用EndRead()來結束。
如果一個線程要寫這個變數,必須先調用BeginWrite()來檢查當前是否有其他現成在寫這個變數並且所有線程是否都調用了EndRead()。只要有一個線程正在寫這個變數,或者有一個線程還沒有調用EndRead(),BeginWrite()將一直等待。當線程寫完以後,必須及時地調用EndWrite(),一邊其他線程可以讀寫這個全域變數。
4、在恰當的位置進行Free,一般在程式的onDestroy裡。