在我的项目中,我有一个线程,可以由线程本身,其他线程或VCL(主应用程序)修改。因此,我使用TCriticalSection。获取/释放每次数据访问。
在正常情况下,下面的代码按预期工作:进入Acquire,与DoCallback同步,然后释放锁。然而,如果任何其他上下文在它已经被锁定的时候获得锁,下面的代码的执行在Synchronize停止-并且,这一次,它不会进入DoCallback方法。
我应该跳过同步方法(即使同步代码调用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;
如果你所谓的"主"线程获取临界区,然后VCL线程(我们通常会称为"主"线程)试图获取它,那么VCL线程将阻塞直到临界区被释放。然后,"主"线程调用Synchronize
,它在VCL线程的上下文中运行给定的函数。由于VCL线程在等待临界区时被阻塞,所以它没有处理消息,因此它无法注意到有一个同步方法要调用。因此,僵局。
Synchronize
之前释放"主"线程中的锁,如果仍然需要的话,之后再重新获取它。如果在synchronized方法中使用的数据需要持续保护以防止同时访问,那么我认为您应该找到一种方法来将数据复制到一个单独的非共享对象中。让同步方法使用该对象而不是共享对象,然后销毁它。
你的评论表明,你可以调用回调函数没有Synchronize
和一切似乎工作。在这种情况下,synchronized方法不是在与以前相同的线程上下文中调用的。它由"主"线程直接调用,而不是由VCL线程调用。这显然减轻了锁争用,但它是否真正安全取决于回调函数做什么。