在编写多线程程序时,有时候会遇到这种情况:
一个线程需要修改某一个数据 而其他线程在高频访问数据,如果直接更改数据,可能导致数据更改不全,因此更改数据的线程需要通知其他所有的线程暂停执行。
因此我写了如下程序:
- {*************************************************************************}
- { }
- { 单元说明: 线程同步执行器 }
- { }
- { 程序作者: missmecn }
- { 程序版本: 1.0 }
- { 程序版权: 版权所有 (C) 2008 }
- { 创建时间: 2008/10/15 }
- { 修改时间: 2008/10/15 }
- { ------------------------------------------------------------------------------------------------------------ }
- { 单元用途: }
- { }
- { 在编写多线程程序时,有时候会遇到这种情况:一个线程需要修改某一个数据 }
- { 而其他线程在高频访问数据,如果直接更改数据,可能导致数据更改不全,因 }
- { 此更改数据的线程需要通知其他所有的线程暂停执行。 }
- { }
- { 注意事项: }
- { }
- { 1. 为什么不用线程的Suspend方法?回答: 因为Suspend过程中可能仍然在读取 }
- { 数据,当线程唤醒时如果数据被更改,特别是数据长度被更改会导致越界访 }
- { 问。 }
- { 2. 为什么每次都去查询线程是否注册? 回答: 因为很多时候我们并不能控制线 }
- { 程的创建和销毁,因此只能在执行期进行查询,虽然会浪费很多CPU。为了 }
- { 节省执行时间,里边特意使用了Hash表。 }
- { 3. 并行执行的线程中的任意一个是否可以执行同步操作? 回答: 可以。内部已 }
- { 经处理了同一线程既是同步发起者也是等待者的情况。 }
- { }
- {*************************************************************************}
- unit ThreadSyncMgr;
- interface
- uses
- Classes, Windows, SyncObjs;
- type
- TThreadSyncMgr = class(TObject)
- const
- MAX_THREAD_COUNT = 256;
- HASH_KEY = 256;
- HASH_ARY_LEN = HASH_KEY * 4;
- private
- type TInnerItem = class(TObject)
- ThreadID: Cardinal;
- Ready: Boolean;
- end;
- private
- FNeedSync: Boolean;
- FSyncThreadID: Cardinal;
- FSyncEvent: TEvent;
- FThreadList: TList;
- FSection: TCriticalSection;
- FThreadHashAry: array[0..HASH_ARY_LEN - 1] of Cardinal; //提高查询效率
- procedure ClearList;
- function IndexOfThread(AThreadID: Cardinal): TInnerItem;
- procedure ResetAllThreadState;
- procedure AddThreadHash(AThreadID: Cardinal);
- function ExistsThreadHash(AThreadID: Cardinal): Boolean;
- // 注册和更新线程状态
- procedure RegisterThread(AThreadID: Cardinal);
- procedure UpdateThreadState(AThreadID: Cardinal; AReady: Boolean);
- function ExistsThread(AThreadID: Cardinal): Boolean;
- // 判断需要同步的线程是否都准备就绪(发起者调用)
- function IsAllThreadReady: Boolean;
- public
- constructor Create;
- destructor Destroy; override;
- // 开始和结束同步(发起者调用)
- function StartSync: Boolean;
- procedure EndSync;
- // 等待同步(等待者在线程循环中调用)
- procedure WaitSync;
- property NeedSync: Boolean read FNeedSync;
- property SyncEvent: TEvent read FSyncEvent;
- end;
- function __ThreadSynMgr: TThreadSyncMgr;
- implementation
- var
- __ThreadSync: TThreadSyncMgr;
- function __ThreadSynMgr: TThreadSyncMgr;
- begin
- Result := __ThreadSync;
- end;
- { TThreadSync }
- procedure TThreadSyncMgr.AddThreadHash(AThreadID: Cardinal);
- var
- LPos: Integer;
- begin
- LPos := AThreadID mod HASH_KEY;
- while FThreadHashAry[LPos] <> 0 do
- Inc(LPos);
- // 未找到合适的位置,退出
- if LPos >= HASH_ARY_LEN then
- Exit;
- FThreadHashAry[LPos] := AThreadID;
- end;
- procedure TThreadSyncMgr.ClearList;
- begin
- while FThreadList.Count > 0 do
- begin
- TInnerItem(FThreadList[0]).Free;
- FThreadList.Delete(0);
- end;
- end;
- constructor TThreadSyncMgr.Create;
- begin
- inherited;
- FNeedSync := False;
- FSyncThreadID := 0;
- FSection := TCriticalSection.Create;
- FSyncEvent := TEvent.Create;
- FThreadList := TList.Create;
- end;
- destructor TThreadSyncMgr.Destroy;
- begin
- ClearList;
- FThreadList.Free;
- FSyncEvent.Free;
- FSection.Free;
- inherited;
- end;
- function TThreadSyncMgr.ExistsThread(AThreadID: Cardinal): Boolean;
- begin
- // 这里查询Hash表是为了提高效率
- Result := ExistsThreadHash(AThreadID);
- end;
- function TThreadSyncMgr.ExistsThreadHash(AThreadID: Cardinal): Boolean;
- var
- LPos: Integer;
- begin
- Result := False;
- LPos := AThreadID mod HASH_KEY;
- while FThreadHashAry[LPos] <> 0 do
- begin
- // 找遍数组仍然没有找到,需要退出
- if LPos >= HASH_ARY_LEN then
- Exit;
- if FThreadHashAry[LPos] = AThreadID then
- begin
- Result := True;
- Break;
- end;
- Inc(LPos);
- end;
- end;
- procedure TThreadSyncMgr.EndSync;
- begin
- // 不是自己锁定的不处理
- if FSyncThreadID <> GetCurrentThreadId then
- Exit;
- if FNeedSync then
- begin
- // 注意: 设置成False状态,需要放到所有参数都重置完毕
- FSyncEvent.SetEvent;
- FSyncThreadID := 0;
- FNeedSync := False;
- end;
- end;
- procedure TThreadSyncMgr.WaitSync;
- var
- LThreadID: Cardinal;
- begin
- LThreadID := GetCurrentThreadId;
- // 判断线程是否注册
- if not ExistsThread(LThreadID) then
- RegisterThread(LThreadID);
- if NeedSync then
- begin
- UpdateThreadState(LThreadID, True);
- SyncEvent.WaitFor(MaxInt);
- end;
- end;
- function TThreadSyncMgr.IndexOfThread(AThreadID: Cardinal): TInnerItem;
- var
- I: Integer;
- begin
- Result := nil;
- for I := 0 to FThreadList.Count - 1 do
- begin
- if TInnerItem(FThreadList[I]).ThreadID = AThreadID then
- begin
- Result := TInnerItem(FThreadList[I]);
- Break;
- end;
- end;
- end;
- function TThreadSyncMgr.IsAllThreadReady: Boolean;
- var
- I: Integer;
- begin
- Result := True;
- FSection.Enter;
- try
- for I := 0 to FThreadList.Count - 1 do
- begin
- // 如果是线程是自己,则忽略
- if TInnerItem(FThreadList[I]).ThreadID = GetCurrentThreadId then
- Continue;
- if not TInnerItem(FThreadList[I]).Ready then
- begin
- Result := False;
- Break;
- end;
- end;
- finally
- FSection.Leave;
- end;
- end;
- procedure TThreadSyncMgr.RegisterThread(AThreadID: Cardinal);
- var
- LItem: TInnerItem;
- begin
- FSection.Enter;
- try
- if IndexOfThread(AThreadID) = nil then
- begin
- LItem := TInnerItem.Create;
- LItem.ThreadID := AThreadID;
- LItem.Ready := False;
- FThreadList.Add(LItem);
- AddThreadHash(AThreadID);
- end;
- finally
- FSection.Leave;
- end;
- end;
- procedure TThreadSyncMgr.ResetAllThreadState;
- var
- I: Integer;
- begin
- FSection.Enter;
- try
- for I := 0 to FThreadList.Count - 1 do
- begin
- TInnerItem(FThreadList[I]).Ready := False;
- end;
- finally
- FSection.Leave;
- end;
- end;
- function TThreadSyncMgr.StartSync: Boolean;
- begin
- Result := False;
- if not FNeedSync then
- begin
- ResetAllThreadState;
- FNeedSync := True;
- FSyncThreadID := GetCurrentThreadId;
- FSyncEvent.ResetEvent;
- // 如果所有线程没有准备好,则循环等待
- while not IsAllThreadReady do
- begin
- Sleep(10);
- end;
- Result := True;
- end;
- end;
- procedure TThreadSyncMgr.UpdateThreadState(AThreadID: Cardinal; AReady: Boolean);
- var
- LItem: TInnerItem;
- begin
- FSection.Enter;
- try
- LItem := IndexOfThread(AThreadID);
- if LItem <> nil then
- begin
- LItem.Ready := AReady;
- end;
- finally
- FSection.Leave;
- end;
- end;
- initialization
- __ThreadSync := TThreadSyncMgr.Create;
- finalization
- __ThreadSync.Free;
- end.
下面是测试代码:
- unit MainFrm;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls, ThreadSyncMgr;
- type
- TMainForm = class(TForm)
- Memo1: TMemo;
- Button1: TButton;
- Button2: TButton;
- procedure Button1Click(Sender: TObject);
- procedure Button2Click(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- end;
- TCtrlThread = class(TThread)
- private
- FMsg: string;
- procedure DoDisplay;
- protected
- procedure Execute; override;
- end;
- TWaitThread = class(TThread)
- private
- FMsg: string;
- procedure DoDisplay;
- protected
- procedure Execute; override;
- end;
- var
- MainForm: TMainForm;
- implementation
- {$R *.dfm}
- uses
- Math;
- { TWaitThread }
- procedure TWaitThread.DoDisplay;
- begin
- MainForm.Memo1.Lines.Add(FMsg);
- end;
- procedure TWaitThread.Execute;
- var
- LTick, LTickA: Cardinal;
- begin
- LTick := 0;
- while not Terminated do
- begin
- // 处理同步
- __ThreadSynMgr.WaitSync;
- // 执行其他命令
- FMsg := Format('%.8d Execute[%d]!', [GetCurrentThreadId, GetTickCount]);
- Synchronize(DoDisplay);
- Sleep(RandomRange(200, 1000));
- end;
- end;
- { TCtrlThread }
- procedure TCtrlThread.DoDisplay;
- begin
- MainForm.Memo1.Lines.Add(FMsg);
- end;
- procedure TCtrlThread.Execute;
- var
- LTick: Cardinal;
- begin
- while not Terminated do
- begin
- FMsg := 'Start Sync';
- Synchronize(DoDisplay);
- LTick := GetTickCount;
- __ThreadSynMgr.StartSync;
- FMsg := Format('Time=%d!', [GetTickCount - LTick]);
- Synchronize(DoDisplay);
- __ThreadSynMgr.EndSync;
- FMsg := 'End Sync';
- Synchronize(DoDisplay);
- end;
- end;
- procedure TMainForm.Button1Click(Sender: TObject);
- begin
- TCtrlThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- TWaitThread.Create(False);
- end;
- procedure TMainForm.Button2Click(Sender: TObject);
- begin
- Memo1.Lines.SaveToFile('c:/1.log');
- end;
- end.