线程事务同步执行器

来源:互联网 发布:武汉淘宝模特招聘 编辑:程序博客网 时间:2024/04/28 02:02

在编写多线程程序时,有时候会遇到这种情况:

  一个线程需要修改某一个数据 而其他线程在高频访问数据,如果直接更改数据,可能导致数据更改不全,因此更改数据的线程需要通知其他所有的线程暂停执行。

因此我写了如下程序:

  1. {*************************************************************************}
  2. {                                                                                                                                                  }
  3. {  单元说明: 线程同步执行器                                                                                                  }
  4. {                                                                                                                                                  }
  5. {  程序作者: missmecn                                                                                                              }
  6. {  程序版本: 1.0                                                                                                                         }
  7. {  程序版权: 版权所有 (C) 2008                                                                                               }
  8. {  创建时间: 2008/10/15                                                                                                            }
  9. {  修改时间: 2008/10/15                                                                                                            }
  10. { ------------------------------------------------------------------------------------------------------------ }
  11. {  单元用途:                                                                                                                               }
  12. {                                                                                                                                                  }
  13. {   在编写多线程程序时,有时候会遇到这种情况:一个线程需要修改某一个数据       }
  14. {   而其他线程在高频访问数据,如果直接更改数据,可能导致数据更改不全,因       }
  15. {   此更改数据的线程需要通知其他所有的线程暂停执行。                                               }
  16. {                                                                                                                                                  }
  17. {  注意事项:                                                                                                                               }
  18. {                                                                                                                                                  }
  19. {   1. 为什么不用线程的Suspend方法?回答: 因为Suspend过程中可能仍然在读取          }
  20. {      数据,当线程唤醒时如果数据被更改,特别是数据长度被更改会导致越界访        }
  21. {      问。                                                                                                                                    }
  22. {   2. 为什么每次都去查询线程是否注册? 回答: 因为很多时候我们并不能控制线          }
  23. {      程的创建和销毁,因此只能在执行期进行查询,虽然会浪费很多CPU。为了        }
  24. {      节省执行时间,里边特意使用了Hash表。                                                                    }
  25. {   3. 并行执行的线程中的任意一个是否可以执行同步操作? 回答: 可以。内部已           }
  26. {      经处理了同一线程既是同步发起者也是等待者的情况。                                            }
  27. {                                                                                                                                                  }
  28. {*************************************************************************}
  29. unit ThreadSyncMgr;
  30. interface
  31. uses
  32.   Classes, Windows, SyncObjs;
  33. type
  34.   
  35.   TThreadSyncMgr = class(TObject)
  36.   const
  37.     MAX_THREAD_COUNT = 256;
  38.     HASH_KEY = 256;
  39.     HASH_ARY_LEN = HASH_KEY * 4;
  40.   private
  41.     type TInnerItem = class(TObject)
  42.       ThreadID: Cardinal;
  43.       Ready: Boolean;
  44.     end;
  45.   private
  46.     FNeedSync: Boolean;
  47.     FSyncThreadID: Cardinal;
  48.     FSyncEvent: TEvent;
  49.     FThreadList: TList;
  50.     FSection: TCriticalSection;
  51.     FThreadHashAry: array[0..HASH_ARY_LEN - 1of Cardinal;  //提高查询效率
  52.     procedure ClearList;
  53.     function IndexOfThread(AThreadID: Cardinal): TInnerItem;
  54.     procedure ResetAllThreadState;
  55.     procedure AddThreadHash(AThreadID: Cardinal);
  56.     function ExistsThreadHash(AThreadID: Cardinal): Boolean;
  57.     // 注册和更新线程状态
  58.     procedure RegisterThread(AThreadID: Cardinal);
  59.     procedure UpdateThreadState(AThreadID: Cardinal; AReady: Boolean);
  60.     function ExistsThread(AThreadID: Cardinal): Boolean;
  61.     // 判断需要同步的线程是否都准备就绪(发起者调用)
  62.     function IsAllThreadReady: Boolean;
  63.   public
  64.     constructor Create;
  65.     destructor Destroy; override;
  66.     // 开始和结束同步(发起者调用)
  67.     function StartSync: Boolean;
  68.     procedure EndSync;
  69.     // 等待同步(等待者在线程循环中调用)
  70.     procedure WaitSync;
  71.     property NeedSync: Boolean read FNeedSync;
  72.     property SyncEvent: TEvent read FSyncEvent;
  73.   end;
  74. function __ThreadSynMgr: TThreadSyncMgr;
  75. implementation
  76. var
  77.   __ThreadSync: TThreadSyncMgr;
  78. function __ThreadSynMgr: TThreadSyncMgr;
  79. begin
  80.   Result := __ThreadSync;
  81. end;
  82. { TThreadSync }
  83. procedure TThreadSyncMgr.AddThreadHash(AThreadID: Cardinal);
  84. var
  85.   LPos: Integer;
  86. begin
  87.   LPos := AThreadID mod HASH_KEY;
  88.   while FThreadHashAry[LPos] <> 0 do
  89.     Inc(LPos);
  90.   // 未找到合适的位置,退出
  91.   if LPos >= HASH_ARY_LEN then
  92.     Exit;
  93.   FThreadHashAry[LPos] := AThreadID;
  94. end;
  95. procedure TThreadSyncMgr.ClearList;
  96. begin
  97.   while FThreadList.Count > 0 do
  98.   begin
  99.     TInnerItem(FThreadList[0]).Free;
  100.     FThreadList.Delete(0);
  101.   end;
  102. end;
  103. constructor TThreadSyncMgr.Create;
  104. begin
  105.   inherited;
  106.   FNeedSync := False;
  107.   FSyncThreadID := 0;
  108.   FSection := TCriticalSection.Create;
  109.   FSyncEvent := TEvent.Create;
  110.   FThreadList := TList.Create;
  111. end;
  112. destructor TThreadSyncMgr.Destroy;
  113. begin
  114.   ClearList;
  115.   FThreadList.Free;
  116.   FSyncEvent.Free;
  117.   FSection.Free;
  118.   inherited;
  119. end;
  120. function TThreadSyncMgr.ExistsThread(AThreadID: Cardinal): Boolean;
  121. begin
  122.   // 这里查询Hash表是为了提高效率
  123.   Result := ExistsThreadHash(AThreadID);
  124. end;
  125. function TThreadSyncMgr.ExistsThreadHash(AThreadID: Cardinal): Boolean;
  126. var
  127.   LPos: Integer;
  128. begin
  129.   Result := False;
  130.   
  131.   LPos := AThreadID mod HASH_KEY;
  132.   while FThreadHashAry[LPos] <> 0 do
  133.   begin
  134.     // 找遍数组仍然没有找到,需要退出
  135.     if LPos >= HASH_ARY_LEN then
  136.       Exit;
  137.     if FThreadHashAry[LPos] = AThreadID then
  138.     begin
  139.       Result := True;
  140.       Break;
  141.     end;
  142.     Inc(LPos);
  143.   end;
  144. end;
  145. procedure TThreadSyncMgr.EndSync;
  146. begin
  147.   // 不是自己锁定的不处理
  148.   if FSyncThreadID <> GetCurrentThreadId then
  149.     Exit;
  150.   
  151.   if FNeedSync then
  152.   begin
  153.     // 注意: 设置成False状态,需要放到所有参数都重置完毕
  154.     FSyncEvent.SetEvent;
  155.     FSyncThreadID := 0;
  156.     FNeedSync := False;
  157.   end;
  158. end;
  159. procedure TThreadSyncMgr.WaitSync;
  160. var
  161.   LThreadID: Cardinal;
  162. begin
  163.   LThreadID := GetCurrentThreadId;
  164.   
  165.   // 判断线程是否注册
  166.   if not ExistsThread(LThreadID) then
  167.     RegisterThread(LThreadID);
  168.   if NeedSync then
  169.   begin
  170.     UpdateThreadState(LThreadID, True);
  171.     SyncEvent.WaitFor(MaxInt);
  172.   end;
  173. end;
  174. function TThreadSyncMgr.IndexOfThread(AThreadID: Cardinal): TInnerItem;
  175. var
  176.   I: Integer;
  177. begin
  178.   Result := nil;
  179.   
  180.   for I := 0 to FThreadList.Count - 1 do
  181.   begin
  182.     if TInnerItem(FThreadList[I]).ThreadID = AThreadID then
  183.     begin
  184.       Result := TInnerItem(FThreadList[I]);
  185.       Break;
  186.     end;
  187.   end;
  188. end;
  189. function TThreadSyncMgr.IsAllThreadReady: Boolean;
  190. var
  191.   I: Integer;
  192. begin
  193.   Result := True;
  194.   FSection.Enter;
  195.   try
  196.     for I := 0 to FThreadList.Count - 1 do
  197.     begin
  198.       // 如果是线程是自己,则忽略
  199.       if TInnerItem(FThreadList[I]).ThreadID = GetCurrentThreadId then
  200.         Continue;
  201.       if not TInnerItem(FThreadList[I]).Ready then
  202.       begin
  203.         Result := False;
  204.         Break;
  205.       end;
  206.     end;
  207.   finally
  208.     FSection.Leave;
  209.   end;
  210. end;
  211. procedure TThreadSyncMgr.RegisterThread(AThreadID: Cardinal);
  212. var
  213.   LItem: TInnerItem;
  214. begin
  215.   FSection.Enter;
  216.   try
  217.     if IndexOfThread(AThreadID) = nil then
  218.     begin
  219.       LItem := TInnerItem.Create;
  220.       LItem.ThreadID := AThreadID;
  221.       LItem.Ready := False;
  222.       FThreadList.Add(LItem);
  223.       AddThreadHash(AThreadID);
  224.     end;
  225.   finally
  226.     FSection.Leave;
  227.   end;
  228. end;
  229. procedure TThreadSyncMgr.ResetAllThreadState;
  230. var
  231.   I: Integer;
  232. begin
  233.   FSection.Enter;
  234.   try
  235.     for I := 0 to FThreadList.Count - 1 do
  236.     begin
  237.       TInnerItem(FThreadList[I]).Ready := False;
  238.     end;
  239.   finally
  240.     FSection.Leave;
  241.   end;
  242. end;
  243. function TThreadSyncMgr.StartSync: Boolean;
  244. begin
  245.   Result := False;
  246.   
  247.   if not FNeedSync then
  248.   begin
  249.     ResetAllThreadState;
  250.     FNeedSync := True;
  251.     FSyncThreadID := GetCurrentThreadId;
  252.     FSyncEvent.ResetEvent;
  253.     // 如果所有线程没有准备好,则循环等待
  254.     while not IsAllThreadReady do
  255.     begin
  256.       Sleep(10);
  257.     end;
  258.     Result := True;
  259.   end;
  260. end;
  261. procedure TThreadSyncMgr.UpdateThreadState(AThreadID: Cardinal; AReady: Boolean);
  262. var
  263.   LItem: TInnerItem;
  264. begin
  265.   FSection.Enter;
  266.   try
  267.     LItem := IndexOfThread(AThreadID);
  268.     if LItem <> nil then
  269.     begin
  270.       LItem.Ready := AReady;    
  271.     end;
  272.   finally
  273.     FSection.Leave;
  274.   end;
  275. end;
  276. initialization
  277.   __ThreadSync := TThreadSyncMgr.Create;
  278. finalization
  279.   __ThreadSync.Free;
  280. end.

下面是测试代码:

  1. unit MainFrm;
  2. interface
  3. uses
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5.   Dialogs, StdCtrls, ThreadSyncMgr;
  6. type
  7.   TMainForm = class(TForm)
  8.     Memo1: TMemo;
  9.     Button1: TButton;
  10.     Button2: TButton;
  11.     procedure Button1Click(Sender: TObject);
  12.     procedure Button2Click(Sender: TObject);
  13.   private
  14.     { Private declarations }
  15.   public
  16.     { Public declarations }
  17.   end;
  18.   TCtrlThread = class(TThread)
  19.  private
  20.     FMsg: string;
  21.     procedure DoDisplay;
  22.   protected
  23.     procedure Execute; override;
  24.   end;
  25.   TWaitThread = class(TThread)
  26.   private
  27.     FMsg: string;
  28.     procedure DoDisplay;
  29.   protected
  30.     procedure Execute; override;
  31.   end;
  32. var
  33.   MainForm: TMainForm;
  34. implementation
  35. {$R *.dfm}
  36. uses
  37.   Math;
  38. { TWaitThread }
  39. procedure TWaitThread.DoDisplay;
  40. begin
  41.   MainForm.Memo1.Lines.Add(FMsg);
  42. end;
  43. procedure TWaitThread.Execute;
  44. var
  45.   LTick, LTickA: Cardinal;
  46. begin
  47.   LTick := 0;
  48.   
  49.   while not Terminated do
  50.   begin
  51.     // 处理同步
  52.     __ThreadSynMgr.WaitSync;
  53.     // 执行其他命令
  54.     FMsg := Format('%.8d Execute[%d]!', [GetCurrentThreadId, GetTickCount]);
  55.     Synchronize(DoDisplay);
  56.     Sleep(RandomRange(2001000));
  57.   end;
  58. end;
  59. { TCtrlThread }
  60. procedure TCtrlThread.DoDisplay;
  61. begin
  62.   MainForm.Memo1.Lines.Add(FMsg);
  63. end;
  64. procedure TCtrlThread.Execute;
  65. var
  66.   LTick: Cardinal;
  67. begin
  68.   while not Terminated do
  69.   begin
  70.     FMsg := 'Start Sync';
  71.     Synchronize(DoDisplay);
  72.     LTick := GetTickCount;
  73.     __ThreadSynMgr.StartSync;
  74.     FMsg := Format('Time=%d!', [GetTickCount - LTick]);
  75.     Synchronize(DoDisplay);
  76.     __ThreadSynMgr.EndSync;
  77.     FMsg := 'End Sync';
  78.     Synchronize(DoDisplay);
  79.   end;
  80. end;
  81. procedure TMainForm.Button1Click(Sender: TObject);
  82. begin
  83.   TCtrlThread.Create(False);
  84.   TWaitThread.Create(False);
  85.   TWaitThread.Create(False);
  86.   TWaitThread.Create(False);
  87.   TWaitThread.Create(False);
  88.   TWaitThread.Create(False);
  89.   TWaitThread.Create(False);
  90.   TWaitThread.Create(False);
  91.   TWaitThread.Create(False);
  92.   TWaitThread.Create(False);
  93.   TWaitThread.Create(False);
  94.   TWaitThread.Create(False);
  95.   TWaitThread.Create(False);
  96.   TWaitThread.Create(False);
  97. end;
  98. procedure TMainForm.Button2Click(Sender: TObject);
  99. begin
  100.   Memo1.Lines.SaveToFile('c:/1.log');
  101. end;
  102. end.