A
Another_eYes
Unregistered / Unconfirmed
GUEST, unregistred user!
先看一个简单的例子:
type
ttest = class(TThread)
protected
procedure Execute;
override;
end;
var
c: TRTLCriticalSection;
implementation
procedure ttest.Execute;
begin
while not terminateddo
begin
sleep(5000);
entercriticalsection(c);
showmessage('aaa');
leavecriticalsection(c);
end;
end;
form上放一个button, OnClick事件代码:
procedure TForm1.Button1Click(Sender: TObject);
begin
initializecriticalsection(c);
entercriticalsection(c);
ttest.create(false);
leavecriticalsection(c);
sleep(1000);
deletecriticalsection(c);
end;
运行一下,点button,会发生什么? 什么也没发生? 嗬嗬,那只意味着一件事:线程死翘翘了。 事实上观察资源管理器可以发现线程已经终止了。
是不是和我有一样的感慨:
不就是等待了一个已经关闭掉的criticalsection嘛,至于杀掉我的线程吗? 郁闷。
而更郁闷的是windows倒是没杀掉我的线程,不过是线程彻底睡去了而已, 最后连程序都关不掉。
话说有一天我做了一个完成端口的程序,开了一堆工作线程处理completionport的notify,为了避免运行时每个socket读写中的冲突,我照正常思维如下定义了一个client:
TClientSocket = class
private
FLock: TRTLCriticalSection;
....
public
constructor Create;
// 调用InitializeCriticalSection初始化FLock
destructor Destroy;
override;
// 调用DeleteCriticalSection释放资源
procedure Lock;
// 调用EnterCriticalSection;
procedure Unlock;
// 调用LeaveCriticalSection;
....
end;
结果发觉运行了一段时间(一段时间≈2分钟--2天半)后程序彻底死翘翘了。郁闷之极地找了2个礼拜的"死锁"而一无所获。无意中察看资源管理器发觉线程数中途减少了,经过上述测试又察看了msdn(不幸我原来没着玩意,该死的delphi的win32帮助中对这个关键点只字不提--即deletecriticalsection释放之后,那些正在等待这个criticalsection的线程的状态成未定义)才发觉原来是EnterCriticalSection在搞鬼。
然后开始思考如何避免这个问题,因为可能一个线程执行socket的write为了不使发送数据紊乱必须加锁输出缓冲区,而在加锁前windows切换线程,执行另一个线程中socket的close请求,执行完后该死的criticalsection已经释放了,等再次切换回前一个线程时那个线程的末日也就到了。考虑了很久都不得其法(包括建一个全局的criticalsection池,这个也会存在问题,具体会出现什么问题你可以想想,呵呵,实践证明不可用),因为我无法确定用户到底什么时候会退出,而执行完退出代码就可能造成某个等待的线程完蛋。
一怒之下抛弃criticalsection自己写了一个lock总算解决了问题:
type
TClient = class
private
FLock: Integer;
FLockCnt: Integer;
.....
end;
function TClient.Lock: Boolean;
var
n, m: Integer;
c: Cardinal;
begin
try
n := GetCurrentThreadID;
result := false;
c := GetTickCount;
while not result and Activedo
begin
m := Integer(InterLockedCompareExchange(Pointer(FLock), Pointer, nil));
result := (m=0) or (m=n);
if result then
Inc(FLockCnt)
else
begin
if GetTickCount-c >1000 then
log('Maybe DeadLock in thread:'+inttostr+' wait for locks locked by thread:'+inttostr(m));
sleep(0);
end;
end;
except
result := false;
end;
end;
procedure Unlock;
begin
try
if InterLockedDecrement(FLockCnt) = 0 then
InterLockedExchange(FLock, 0);
except
end;
end;
type
ttest = class(TThread)
protected
procedure Execute;
override;
end;
var
c: TRTLCriticalSection;
implementation
procedure ttest.Execute;
begin
while not terminateddo
begin
sleep(5000);
entercriticalsection(c);
showmessage('aaa');
leavecriticalsection(c);
end;
end;
form上放一个button, OnClick事件代码:
procedure TForm1.Button1Click(Sender: TObject);
begin
initializecriticalsection(c);
entercriticalsection(c);
ttest.create(false);
leavecriticalsection(c);
sleep(1000);
deletecriticalsection(c);
end;
运行一下,点button,会发生什么? 什么也没发生? 嗬嗬,那只意味着一件事:线程死翘翘了。 事实上观察资源管理器可以发现线程已经终止了。
是不是和我有一样的感慨:
不就是等待了一个已经关闭掉的criticalsection嘛,至于杀掉我的线程吗? 郁闷。
而更郁闷的是windows倒是没杀掉我的线程,不过是线程彻底睡去了而已, 最后连程序都关不掉。
话说有一天我做了一个完成端口的程序,开了一堆工作线程处理completionport的notify,为了避免运行时每个socket读写中的冲突,我照正常思维如下定义了一个client:
TClientSocket = class
private
FLock: TRTLCriticalSection;
....
public
constructor Create;
// 调用InitializeCriticalSection初始化FLock
destructor Destroy;
override;
// 调用DeleteCriticalSection释放资源
procedure Lock;
// 调用EnterCriticalSection;
procedure Unlock;
// 调用LeaveCriticalSection;
....
end;
结果发觉运行了一段时间(一段时间≈2分钟--2天半)后程序彻底死翘翘了。郁闷之极地找了2个礼拜的"死锁"而一无所获。无意中察看资源管理器发觉线程数中途减少了,经过上述测试又察看了msdn(不幸我原来没着玩意,该死的delphi的win32帮助中对这个关键点只字不提--即deletecriticalsection释放之后,那些正在等待这个criticalsection的线程的状态成未定义)才发觉原来是EnterCriticalSection在搞鬼。
然后开始思考如何避免这个问题,因为可能一个线程执行socket的write为了不使发送数据紊乱必须加锁输出缓冲区,而在加锁前windows切换线程,执行另一个线程中socket的close请求,执行完后该死的criticalsection已经释放了,等再次切换回前一个线程时那个线程的末日也就到了。考虑了很久都不得其法(包括建一个全局的criticalsection池,这个也会存在问题,具体会出现什么问题你可以想想,呵呵,实践证明不可用),因为我无法确定用户到底什么时候会退出,而执行完退出代码就可能造成某个等待的线程完蛋。
一怒之下抛弃criticalsection自己写了一个lock总算解决了问题:
type
TClient = class
private
FLock: Integer;
FLockCnt: Integer;
.....
end;
function TClient.Lock: Boolean;
var
n, m: Integer;
c: Cardinal;
begin
try
n := GetCurrentThreadID;
result := false;
c := GetTickCount;
while not result and Activedo
begin
m := Integer(InterLockedCompareExchange(Pointer(FLock), Pointer, nil));
result := (m=0) or (m=n);
if result then
Inc(FLockCnt)
else
begin
if GetTickCount-c >1000 then
log('Maybe DeadLock in thread:'+inttostr+' wait for locks locked by thread:'+inttostr(m));
sleep(0);
end;
end;
except
result := false;
end;
end;
procedure Unlock;
begin
try
if InterLockedDecrement(FLockCnt) = 0 then
InterLockedExchange(FLock, 0);
except
end;
end;