为什么我的多线程应用程序有时会在关闭时挂起?

Sha*_*ews 1 delphi multithreading critical-section

我在我的应用程序中使用了几个关键部分.关键部分可防止大数据blob被不同的线程同时修改和访问.

AFAIK它正常工作,除非有时应用程序在退出时挂起.我想知道这是否与我对关键部分的使用有关.

有没有一种正确的方法来在析构函数中释放TCriticalSection对象?

感谢所有的答案.我正在考虑这些新信息,再次查看我的代码.干杯!

Del*_*ics 7

正如Rob所说,唯一的要求是确保关键部分当前不属于任何线程.甚至没有关于破坏它的线程.因此,正确销毁TCriticalSection没有任何模式可供遵循.只有您的应用程序必须采取的必要行为才能确保发生.

如果你的应用程序是锁定的,那么我怀疑它是任何负责任的关键部分的自由.正如MSDN所说(在Rob发布的链接中),DeleteCriticalSection()(最终解放了TCriticalSection调用)不会阻止任何线程.

如果您正在释放其他线程仍在尝试访问的关键部分,那么您将获得访问冲突和其他意外行为,而不是死锁,因为这个小代码示例应该可以帮助您演示:

implementation

uses
  syncobjs;


  type
    tworker = class(tthread)
    protected
      procedure Execute; override;
    end;


  var
    cs: TCriticalSection;
    worker: Tworker;


procedure TForm2.FormCreate(Sender: TObject);
begin
  cs := TCriticalSection.Create;

  worker := tworker.Create(true);
  worker.FreeOnTerminate := TRUE;
  worker.Start;

  sleep(5000);

  cs.Enter;

  showmessage('will AV before you see this');
end;

{ tworker }

procedure tworker.Execute;
begin
  inherited;
  cs.Free;
end;
Run Code Online (Sandbox Code Playgroud)

添加到表单的实现单元,根据需要更正FormCreate()事件处理程序的"TForm2"引用.

在FormCreate()中,这将创建一个关键部分,然后启动一个线程,其唯一目的是释放该部分.我们引入Sleep()延迟以给线程初始化和执行时间,然后我们尝试自己进入临界区.

我们当然不能因为它是免费的.但是我们的代码没有挂起 - 它没有死锁,试图访问其他东西所拥有的资源,它只是爆炸,因为我们正在尝试访问不再存在的资源.

你可以更加确定在这个场景中通过NIL在免费获得关键部分参考时创建一个AV.

现在,尝试将FormCreate()代码更改为:

  cs := TCriticalSection.Create;

  worker := tworker.Create(true);
  worker.FreeOnTerminate := TRUE;

  cs.Enter;
  worker.Start;

  sleep(5000);

  cs.Leave;

  showmessage('appearances can be deceptive');
Run Code Online (Sandbox Code Playgroud)

这改变了一些事情......现在主线程将获得关键部分的所有权 - 工作线程现在将释放临界区,而它仍由主线程拥有.

但是,在这种情况下,对cs.Leave的调用不一定会导致访问冲突.在这种情况下(afaict)发生的所有情况都是允许拥有的线程按照预期"离开"该部分(当然不是,因为该部分已经消失,但它似乎在线程中离开之前输入的部分)...

...在更复杂的情况下,可能会发生访问冲突或其他错误,因为以前用于临界区对象的内存可能会在您调用它的Leave()方法时重新分配给其他对象,从而导致一些调用其他一些未知对象或访问无效内存等.

再次,更改worker.Execute()以便在释放之后NIL是关键部分ref将确保在尝试调用cs.Leave()时发生访问冲突,因为Leave()调用Release()和Release()是虚拟的 - 调用具有NIL引用的虚方法保证AV(对于调用虚拟Acquire()方法的Enter()同上).

在任何情况下:

最坏情况:异常或奇怪的行为

"最佳"案例:拥有线程似乎认为它已正常"离开"该部分.

在任何情况下都不会发生死锁或挂起,只是因为当一个线程中的临界区域被释放时,其他线程随后尝试进入或离开该临界区域时.

所有这些都是一种圆润的说法,听起来你的线程代码中有一个更基本的竞争条件,与你的关键部分的自由关系没有直接关系.

无论如何,我希望我的一些调查工作可能会让你走上正确的道路.