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

锁和对象一起封装的危险

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

定义接口:

  1. type  
  2.   TLockObject = class;  
  3.   TLockTestThread = class;  
  4.   TForm1 = class(TForm)  
  5.     btn1: TButton;  
  6.     mmoLockThreadTest: TMemo;  
  7.     procedure btn1Click(Sender: TObject);  
  8.     procedure FormCreate(Sender: TObject);  
  9.   private  
  10.     FLockTestThread: TLockTestThread;  
  11.     FLockObject: TLockObject;  
  12.   public  
  13.     { Public declarations }  
  14.   end;  
  15.   
  16.   TLockTestThread = class(TThread)  
  17.   private  
  18.     FLockObject: TLockObject;  
  19.   public  
  20.     procedure Execute; override;  
  21.     procedure AddLockLog;  
  22.     property LockObject: TLockObject read FLockObject write FLockObject;  
  23.   end;  
  24.   
  25.   TLockObject = class(TObject)  
  26.   private  
  27.     FLock: TCriticalSection;  
  28.   public  
  29.     constructor Create; virtual;  
  30.     destructor Destroy; override;  
  31.     procedure Lock;  
  32.     procedure UnLock;  
  33.   end;  
  34.   
  35. var  
  36.   Form1: TForm1;  
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创建的时候创建锁,并把锁锁住,创建一个线程等待锁返回:

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

  1. procedure TLockTestThread.AddLockLog;  
  2. begin  
  3.   Form1.mmoLockThreadTest.Lines.Add('Lock')  
  4. end;  
  5.   
  6. procedure TLockTestThread.Execute;  
  7. begin  
  8.   inherited;  
  9.   while not Terminated do  
  10.   begin  
  11.     FLockObject.Lock;  
  12.     Synchronize(AddLockLog);  
  13.   end;  
  14. 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释放,线程也会一直等待,造成死锁。

  1. procedure TForm1.btn1Click(Sender: TObject);  
  2. begin  
  3.   FLockObject.Lock;  
  4.   FLockObject.Free;  
  5.   FLockObject := nil;  
  6. end;  
procedure TForm1.btn1Click(Sender: TObject);
begin
  FLockObject.Lock;
  FLockObject.Free;
  FLockObject := nil;
end;

锁和对象分离

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

  1. {* 客户端对应Socket管理对象 *}  
  2. TSocketHandles = class(TObject)  
  3. private  
  4.   {* 正在使用列表管理对象 *}  
  5.   FList: TList;  
  6.   {* 不再使用列表管理对象 *}  
  7.   FIdleList: TList;  
  8.   {* 锁 *}  
  9.   FLock: TCriticalSection;  
  10.   {* 获取某一个 *}  
  11.   function GetItems(const AIndex: Integer): PClientSocket;  
  12.   {* 获取总个数 *}  
  13.   function GetCount: Integer;  
  14.   {* 清除 *}  
  15.   procedure Clear;  
  16. public  
  17.   constructor Create; virtual;  
  18.   destructor Destroy; override;  
  19.   {* 加锁 *}  
  20.   procedure Lock;  
  21.   {* 解锁 *}  
  22.   procedure UnLock;  
  23.   {* 添加一个对象 *}  
  24.   function Add(ASocketHandle: TSocketHandle): Integer;  
  25.   {* 删除 *}  
  26.   procedure Delete(const AIndex: Integer); overload;  
  27.   procedure Delete(ASocketHandle: TSocketHandle); overload;  
  28.   property Items[const AIndex: Integer]: PClientSocket read GetItems; default;  
  29.   property Count: Integer read GetCount;  
  30. end;  
  {* 客户端对应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;
实现单元:
  1. { TSocketHandles }  
  2.   
  3. constructor TSocketHandles.Create;  
  4. begin  
  5.   FList := TList.Create;  
  6.   FIdleList := TList.Create;  
  7.   FLock := TCriticalSection.Create;  
  8. end;  
  9.   
  10. destructor TSocketHandles.Destroy;  
  11. begin  
  12.   Clear;  
  13.   FList.Free;  
  14.   FIdleList.Free;  
  15.   FLock.Free;  
  16.   inherited;  
  17. end;  
  18.   
  19. function TSocketHandles.GetItems(const AIndex: Integer): PClientSocket;  
  20. begin  
  21.   Result := FList[AIndex];  
  22. end;  
  23.   
  24. function TSocketHandles.GetCount: Integer;  
  25. begin  
  26.   Result := FList.Count;  
  27. end;  
  28.   
  29. procedure TSocketHandles.Clear;  
  30. var  
  31.   i: Integer;  
  32.   ClientSocket: PClientSocket;  
  33. begin  
  34.   for i := 0 to Count - 1 do  
  35.   begin  
  36.     ClientSocket := Items[i];  
  37.     ClientSocket.Lock.Free;  
  38.     ClientSocket.SocketHandle.Free;  
  39.     Dispose(ClientSocket);  
  40.   end;  
  41.   FList.Clear;  
  42.   for i := 0 to FIdleList.Count - 1 do  
  43.   begin  
  44.     ClientSocket := FIdleList[i];  
  45.     ClientSocket.Lock.Free; //释放锁   
  46.     Dispose(ClientSocket);  
  47.   end;  
  48.   FIdleList.Clear;  
  49. end;  
  50.   
  51. procedure TSocketHandles.Lock;  
  52. begin  
  53.   FLock.Enter;  
  54. end;  
  55.   
  56. procedure TSocketHandles.UnLock;  
  57. begin  
  58.   FLock.Leave;  
  59. end;  
  60.   
  61. function TSocketHandles.Add(ASocketHandle: TSocketHandle): Integer;  
  62. var  
  63.   ClientSocket: PClientSocket;  
  64. begin  
  65.   if FIdleList.Count > 0 then //先在空余中查找   
  66.   begin  
  67.     ClientSocket := FIdleList[0];  
  68.     FIdleList.Delete(0);  
  69.   end  
  70.   else //否则创建一个   
  71.   begin  
  72.     New(ClientSocket);  
  73.     ClientSocket.Lock := TCriticalSection.Create;  
  74.   end;  
  75.   ClientSocket.SocketHandle := ASocketHandle;  
  76.   ASocketHandle.FLock := ClientSocket.Lock;  
  77.   Result := FList.Add(ClientSocket);  
  78. end;  
  79.   
  80. procedure TSocketHandles.Delete(const AIndex: Integer);  
  81. var  
  82.   ClientSocket: PClientSocket;  
  83. begin  
  84.   ClientSocket := FList[AIndex];  
  85.   ClientSocket.Lock.Enter;  
  86.   try  
  87.     ClientSocket.SocketHandle.Free;  
  88.     ClientSocket.SocketHandle := nil;  
  89.   finally  
  90.     ClientSocket.Lock.Leave;  
  91.   end;  
  92.   FList.Delete(AIndex);  
  93.   if FIdleList.Count > MAX_IDLELOCK then //如果达到最大空闲个数,则释放   
  94.     Dispose(ClientSocket)  
  95.   else  
  96.     FIdleList.Add(ClientSocket);  
  97. end;  
  98.   
  99. procedure TSocketHandles.Delete(ASocketHandle: TSocketHandle);  
  100. var  
  101.   i, iIndex: Integer;  
  102. begin  
  103.   iIndex := -1;  
  104.   for i := 0 to Count - 1 do  
  105.   begin  
  106.     if Items[i].SocketHandle = ASocketHandle then  
  107.     begin  
  108.       iIndex := i;  
  109.       Break;  
  110.     end;  
  111.   end;  
  112.   if iIndex <> -1 then  
  113.   begin  
  114.     Delete(iIndex);  
  115.   end;  
  116. 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;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值