终止线程时,Delphi AsyncCalls死锁(com + dll完成) [英] Delphi AsyncCalls Deadlock when Terminating the Threads (com+ dll finalization)

查看:257
本文介绍了终止线程时,Delphi AsyncCalls死锁(com + dll完成)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个很大的Delphi 2007项目,并且使用

I have a big Delphi 2007 project, and I use AsyncCall

我已经在一个控制台应用程序中提取并测试了多线程代码,并且一切正常.

I have extracted and tested the multithreading code in a single console application and everything works fine.

我的Delphi 2007项目生成一个COM DLL,我有一些用C#编写的单元测试-调用该DLL的mstest.

My Delphi 2007 project produces a COM DLL and I have some unit tests that are written in C# - mstest that calls the DLL.

单元测试经常随机挂起(通过测试),我发现它发生在AsyncCall单元-finalizatoin部分,该线程释放了ThreadPool.

The unit tests hang quite often randomly (tests passed) and i found out it happens at the AsyncCall unit - finalizatoin section that frees the ThreadPool.

destructor TThreadPool.Destroy;
var
  I: Integer;
  Call: TInternalAsyncCall;
begin
  FMaxThreads := FThreadCount; // Do not allocation new threads
  FDestroying := True; // => Sync in this thread because there is no other thread (required for FAsnycCallHead.Free)

  // Allow the threads to terminate if there is no task
  for I := FThreadCount - 1 downto 0 do
    FThreads[I].Terminate;
  // Wake up all sleeping threads and keep them awake so they can terminate
  SetEvent(FThreadTerminateEvent);
  // Wait and destroy the threads
****Hangs here -------->      for I := FThreadCount - 1 downto 0 do   
****------->        FThreads[I].Free;

  ReleaseAutoDeleteAsyncCalls;

  // Clean up not yet released AutoDelete InternalAsyncCalls.
  while FAsyncCallHead <> nil do
  begin
    Call := FAsyncCallHead.FNext;
    CheckAutoDelete(FAsyncCallHead);
    FAsyncCallHead := Call;
  end;

  CloseHandle(FThreadTerminateEvent);
  CloseHandle(FWakeUpEvent);
  CloseHandle(FMainThreadSyncEvent);
  DeallocateHWnd(FMainThreadVclHandle);
  DeleteCriticalSection(FAsyncCallsCritSect);

  inherited Destroy;
end;

如果我删除

  for I := FThreadCount - 1 downto 0 do
    FThreads[I].Free;

然后正常工作.我无法在一个简单的示例中重现它,也无法想到终止时可能导致线程挂起的任何原因.

It then works fine. I can't reproduce this in a simple example and can't think of any reasons that could hang the threads when terminating.

我相信这与这里提出的问题非常相似:

I believe it is very similar to the issues raised here:

http://qc.embarcadero.com/wc/qcmain.aspx? d = 29843

如果线程停止,它将从Classes:ThreadProc调用ThreadExit ThreadExit用DLL_THREAD_DETACH调用DLLEntryProc.只有一个 进程中的线程可以在DLL初始化或分离例程中 一次.

If a thread stops, it calls ThreadExit from Classes:ThreadProc And ThreadExit calls the DLLEntryProc with DLL_THREAD_DETACH. Only one thread in a process can be in a DLL initialization or detach routine at a time.

问题是com + dll的最终确定是从 DLL_THREAD_DETACH,以便Classes:TThread.WaitFor将等待 不确定地是因为线程不会因为无法处理而终止 它的DLL_THREAD_DETACH,直到完成完成为止.

The problem is that the finalization of a com+ dll is run from the DLL_THREAD_DETACH, so that the Classes:TThread.WaitFor will wait indefinitly because the thread won't terminate because it can't handle its DLL_THREAD_DETACH till the finalization finishes.

更新

我花了几天的时间找出原因,并且有一个解决方法是手动执行AsyncCall单元的完成部分.

I have spent several days finding out the causes, and there is a work around which is to execute the finalization section of AsyncCall unit manually.

这与算法无关,因为即使是非常简单的代码也可能导致com + dll出现问题.

It is nothing to do with the algorithms, because even very simple code can cause problem in com+ dll.

以下是复制步骤:

创建一个com + dll 创建一个具有以下代码的方法: 确保已调用TestAsync,当线程池(来自AsyncCall)正在清理线程时,您会注意到死锁.

create a com+ dll create a method that has the following code: make sure TestAsync is called and you will notice the deadlock when the thread pool (from AsyncCall) is cleaning up the threads.

procedure Test(int i: integer); cdecl;
begin
  Exit;
end;

procedure TestAsyn;
var
  t: IAsyncCall;
begin
  t := AsyncCall(@Test, [0])
  t.Sync;
end;

推荐答案

似乎您正在尝试执行在dll的初始化/完成过程中不允许执行的操作.

It looks like you're trying to do something that you're not allowed to do within initialization/finalization of a dll.

您需要从其他地方运行该完成代码.通常,这是在dll导出的辅助函数中完成的.另一种选择是在释放dll的最后一个com对象时运行终结处理.

You'll need to run that finalization code from somewhere else. Usually this is done inside of a helper function that the dll exports. The other option is to run the finalization when the last com object of your dll is freed.

导出的函数 DllCanUnloadNow 看起来像是终止线程的候选者.

The exported function DllCanUnloadNow looks like a candidate for terminating the threads.

这篇关于终止线程时,Delphi AsyncCalls死锁(com + dll完成)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆