4

在我的项目中,我有一个可能由线程本身、其他线程或 VCL(主应用程序)修改的线程。因此,我对每次数据访问都使用 TCriticalSection.Acquire / Release。

正常情况下,下面的代码按预期工作:进入Acquire,Synchronizes with 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;
4

1 回答 1

9

如果你所谓的“主”线程获取了临界区,然后 VCL 线程(也就是我们通常所说的“主”线程)尝试获取它,那么 VCL 线程将阻塞,直到临界区被释放. 然后你的“主”线程调用Synchronize,它在 VCL 线程的上下文中运行给定的函数。由于 VCL 线程在临界区被阻塞等待,它没有处理消息,所以它不会注意到有一个同步的方法可以调用。于是,陷入僵局。

不要在线程内调用之间持有锁。在调用之前释放“主”线程中的锁Synchronize,然后在之后重新获取它,如果你仍然需要它。如果同步方法中使用的数据需要持续保护以防止同时访问,那么我认为您应该找到一种方法将数据复制到单独的非共享对象中。让同步方法使用该对象而不是共享对象,然后将其销毁。

您的评论表明您可以在没有的情况下调用回调函数,Synchronize并且一切似乎都可以正常工作。在这种情况下,同步方法不会在与以前相同的线程上下文中被调用。它由您的“主”线程直接调用,而不是由 VCL 线程调用。这显然缓解了锁争用,但它是否真的安全取决于回调函数的作用。

于 2011-11-14T17:01:03.863 回答