DELPHI高性能大容量SOCKET并发(五):锁和对象分离

来源:互联网 发布:超市必买清单知乎 编辑:程序博客网 时间:2024/05/17 04:32

锁和对象一起封装的危险

在多线程编写中,绝大多数编码风格喜欢把锁和要访问的对象封装在同一个对象,释放对象的时候也释放锁,这样会造成死锁。我们写一个测试例子,我们创建一个锁,把锁锁住,然后再创建一个线程,一直不停的等待锁返回,然后我们把锁释放,这时线程就死锁,代码如下:

定义接口:

type  TLockObject = class;  TLockTestThread = class;  TForm1 = class(TForm)    btn1: TButton;    mmoLockThreadTest: TMemo;    procedure btn1Click(Sender: TObject);    procedure FormCreate(Sender: TObject);  private    FLockTestThread: TLockTestThread;    FLockObject: TLockObject;  public    { Public declarations }  end;  TLockTestThread = class(TThread)  private    FLockObject: TLockObject;  public    procedure Execute; override;    procedure AddLockLog;    property LockObject: TLockObject read FLockObject write FLockObject;  end;  TLockObject = class(TObject)  private    FLock: TCriticalSection;  public    constructor Create; virtual;    destructor Destroy; override;    procedure Lock;    procedure UnLock;  end;var  Form1: TForm1;
在Form1创建的时候创建锁,并把锁锁住,创建一个线程等待锁返回:

procedure TForm1.FormCreate(Sender: TObject);begin  FLockObject := TLockObject.Create;  FLockObject.Lock;  FLockTestThread := TLockTestThread.Create(True);  FLockTestThread.LockObject := FLockObject;  FLockTestThread.FreeOnTerminate := True;  FLockTestThread.Resume;end;
线程的执行方法一直等待锁返回,并写一条日志。由于是窗体创建的时候,锁已经锁住了,因此线程会一直等待:

procedure TLockTestThread.AddLockLog;begin  Form1.mmoLockThreadTest.Lines.Add('Lock')end;procedure TLockTestThread.Execute;begin  inherited;  while not Terminated do  begin    FLockObject.Lock;    Synchronize(AddLockLog);  end;end;
这时线程会一直等待,如果我们把FLockObject释放,线程也会一直等待,造成死锁。

procedure TForm1.btn1Click(Sender: TObject);begin  FLockObject.Lock;  FLockObject.Free;  FLockObject := nil;end;

锁和对象分离

有了上面的基础之后,我们就需要把锁和对象分离,在IOCPDemoSvr例子代码中TSocketHandle我们用一个结构体来管理锁和对象,锁在创建之后只有等TSocketHandle释放之后再释放,主要代码是TSocketHandles类,定义单元:

  {* 客户端对应Socket管理对象 *}  TSocketHandles = class(TObject)  private    {* 正在使用列表管理对象 *}    FList: TList;    {* 不再使用列表管理对象 *}    FIdleList: TList;    {* 锁 *}    FLock: TCriticalSection;    {* 获取某一个 *}    function GetItems(const AIndex: Integer): PClientSocket;    {* 获取总个数 *}    function GetCount: Integer;    {* 清除 *}    procedure Clear;  public    constructor Create; virtual;    destructor Destroy; override;    {* 加锁 *}    procedure Lock;    {* 解锁 *}    procedure UnLock;    {* 添加一个对象 *}    function Add(ASocketHandle: TSocketHandle): Integer;    {* 删除 *}    procedure Delete(const AIndex: Integer); overload;    procedure Delete(ASocketHandle: TSocketHandle); overload;    property Items[const AIndex: Integer]: PClientSocket read GetItems; default;    property Count: Integer read GetCount;  end;
实现单元:
{ TSocketHandles }constructor TSocketHandles.Create;begin  FList := TList.Create;  FIdleList := TList.Create;  FLock := TCriticalSection.Create;end;destructor TSocketHandles.Destroy;begin  Clear;  FList.Free;  FIdleList.Free;  FLock.Free;  inherited;end;function TSocketHandles.GetItems(const AIndex: Integer): PClientSocket;begin  Result := FList[AIndex];end;function TSocketHandles.GetCount: Integer;begin  Result := FList.Count;end;procedure TSocketHandles.Clear;var  i: Integer;  ClientSocket: PClientSocket;begin  for i := 0 to Count - 1 do  begin    ClientSocket := Items[i];    ClientSocket.Lock.Free;    ClientSocket.SocketHandle.Free;    Dispose(ClientSocket);  end;  FList.Clear;  for i := 0 to FIdleList.Count - 1 do  begin    ClientSocket := FIdleList[i];    ClientSocket.Lock.Free; //释放锁    Dispose(ClientSocket);  end;  FIdleList.Clear;end;procedure TSocketHandles.Lock;begin  FLock.Enter;end;procedure TSocketHandles.UnLock;begin  FLock.Leave;end;function TSocketHandles.Add(ASocketHandle: TSocketHandle): Integer;var  ClientSocket: PClientSocket;begin  if FIdleList.Count > 0 then //先在空余中查找  begin    ClientSocket := FIdleList[0];    FIdleList.Delete(0);  end  else //否则创建一个  begin    New(ClientSocket);    ClientSocket.Lock := TCriticalSection.Create;  end;  ClientSocket.SocketHandle := ASocketHandle;  ASocketHandle.FLock := ClientSocket.Lock;  Result := FList.Add(ClientSocket);end;procedure TSocketHandles.Delete(const AIndex: Integer);var  ClientSocket: PClientSocket;begin  ClientSocket := FList[AIndex];  ClientSocket.Lock.Enter;  try    ClientSocket.SocketHandle.Free;    ClientSocket.SocketHandle := nil;  finally    ClientSocket.Lock.Leave;  end;  FList.Delete(AIndex);  if FIdleList.Count > MAX_IDLELOCK then //如果达到最大空闲个数,则释放    Dispose(ClientSocket)  else    FIdleList.Add(ClientSocket);end;procedure TSocketHandles.Delete(ASocketHandle: TSocketHandle);var  i, iIndex: Integer;begin  iIndex := -1;  for i := 0 to Count - 1 do  begin    if Items[i].SocketHandle = ASocketHandle then    begin      iIndex := i;      Break;    end;  end;  if iIndex <> -1 then  begin    Delete(iIndex);  end;end;

V1版下载地址:http://download.csdn.net/detail/sqldebug_fan/4510076,需要资源10分,有稳定性问题,可以作为研究稳定性用;

V2版下载地址:http://download.csdn.net/detail/sqldebug_fan/5560185,不需要资源分,解决了稳定性问题和提高性能;免责声明:此代码只是为了演示IOCP编程,仅用于学习和研究,切勿用于商业用途。水平有限,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com
原创粉丝点击