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

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

锁和对象一起封装的危险

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

定义接口:

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;

IOCPDemoSvr下载地址:http://download.csdn.net/detail/sqldebug_fan/4510076

免责声明:此代码只是为了演示IOCP编程,仅用于学习和研究,切勿用于商业用途。水平有限,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com