您的位置:首页 > 其它

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

2012-09-20 16:28 465 查看
锁和对象一起封装的危险

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

定义接口:

[delphi]
view plaincopyprint?

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;

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创建的时候创建锁,并把锁锁住,创建一个线程等待锁返回:

[delphi]
view plaincopyprint?

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

[delphi]
view plaincopyprint?

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;

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释放,线程也会一直等待,造成死锁。

[delphi]
view plaincopyprint?

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

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


锁和对象分离

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

[delphi]
view plaincopyprint?

{* 客户端对应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;

{* 客户端对应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;
实现单元:

[delphi]
view plaincopyprint?

{ 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;

{ 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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: