Delphi 线程:CriticalSection 未“释放”在其方法内使用 Synchronize 时
在我的项目中,我有一个线程,它可能会被线程本身、其他线程或 VCL(主应用程序)修改。 因此,我对每次数据访问都使用 TCriticalSection.Acquire / Release。
正常情况下,下面的代码会按预期工作:进入Acquire,与DoCallback同步,然后释放锁。 但是,如果任何其他上下文在已锁定时获取锁定,则以下代码的执行将在 Synchronize 处停止 - 并且,这一次,它不会进入 DoCallback 方法。
我应该跳过 Synchronize 方法(即使 Synchronize 代码调用 VCL)并依赖于 CriticalSection 本身吗?这种行为的原因是什么?
主线程代码:
fData:= nil;
try
fSingleRequest.Acquire;
if fItem <> nil then
begin
fData:= fItem.Request;
SubmitRequest();
fCallbackData:= fItem.fExtraData;
fCallback:= fItem.fCallback;
Synchronize(DoCallback); // <-- this line is called
end;
finally
fSingleRequest.Release; // <-- this isn't under the specific situation
end;
In my project I have a Thread which might be modiefied by the thread itself, other thread or VCL (main app).
Thus I'm using TCriticalSection.Acquire / Release for every data access.
Under normal circumstances, the below code works as expected: enters Acquire, Synchronizes with DoCallback, then releases the lock.
However, if any of other contexts Acquires the lock at the time it was already locked, the execution of below code stops at Synchronize - and, this time, it DOES NOT enter the DoCallback method.
Should I skip Synchronize method (even though the Synchronize'd code calls VCL) and rely on the CriticalSection itself? What is the reason of this behavior?
code of the main thread:
fData:= nil;
try
fSingleRequest.Acquire;
if fItem <> nil then
begin
fData:= fItem.Request;
SubmitRequest();
fCallbackData:= fItem.fExtraData;
fCallback:= fItem.fCallback;
Synchronize(DoCallback); // <-- this line is called
end;
finally
fSingleRequest.Release; // <-- this isn't under the specific situation
end;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果你所谓的“主”线程获取临界区,然后VCL线程(我们通常称之为“主”线程)尝试获取它,那么VCL线程将阻塞直到关键部分被释放。然后,您的“主”线程调用 Synchronize,它在 VCL 线程的上下文中运行给定的函数。由于 VCL 线程被阻塞在临界区等待,因此它不处理消息,因此它无法注意到有一个同步方法要调用。于是,陷入僵局。
不要在线程内调用之间持有锁。在调用 Synchronize 之前释放“主”线程中的锁,然后在之后重新获取它(如果您仍然需要它)。如果同步方法中使用的数据需要持续保护以防止同时访问,那么我认为您应该找到一种方法将数据复制到单独的非共享对象中。让同步方法使用该对象而不是共享对象,然后销毁它。
您的评论表明您可以在没有
Synchronize
的情况下调用回调函数,并且一切似乎都正常。在这种情况下,同步方法不会像以前一样在同一线程上下文中调用。它是由“主”线程而不是 VCL 线程直接调用的。这显然缓解了锁争用,但是否真的安全取决于回调函数的作用。If your so-called "main" thread acquires the critical section, and then the VCL thread (which is what we would usually call the "main" thread) attempts to acquire it, then the VCL thread will block until the critical section is released. Then your "main" thread calls
Synchronize
, which runs the given function in the context of the VCL thread. Since the VCL thread is blocked waiting on the critical section, it's not processing messages, so it can't notice that there's a synchronized method to call. Thus, deadlock.Don't hold the lock across intra-thread calls. Release the lock in your "main" thread before you call
Synchronize
, and then re-acquire it afterward, if you still need it. If the data being used in the synchronized method needs continued protection from simultaneous access, then I think you should find a way to copy the data into a separate, non-shared object. Have the synchronized method use that object instead of the shared one, and then destroy it afterward.Your comment indicates that you can call the callback function without
Synchronize
and everything appears to work. In that case, the synchronized method isn't being called in the same thread context as before. It's being called directly by your "main" thread instead of by the VCL thread. That obviously relieves the lock contention, but whether it's really safe depends on what the callback function does.