在这种情况下,何时需要调用CoInitialize()?

时间:2012-02-15 01:11:02

标签: multithreading delphi activex ado delphi-xe2

我正在Delphi XE2中构建一个多线程Windows服务应用程序,它使用ADO数据库组件连接到SQL Server。我在线程内部使用了CoInitialize(nil);很多次,但在这种情况下,我有一个我不确定的函数。

此函数称为TryConnect,它尝试使用给定的连接字符串连接到数据库。它在连接成功时返回true或false。问题是这个函数将在主服务线程的内部和外部使用,并且它将创建自己的临时TADOConnection组件,这需要CoInitialize ...

我的问题是我是否需要在此功能中调用CoInitialize?如果我这样做,并且由于服务的执行过程也使用CoInitialize,如果我从服务中调用此函数,它们会干扰吗? TryConnect函数位于从主服务线程创建的对象内部(但最终将移动到其自己的线程)。我需要知道从同一个线程(和CoInitialize())调用CoUninitialize两次是否会干扰 - 以及如何正确处理这种情况。

以下是以下代码......

//This is the service app's execute procedure
procedure TJDRMSvr.ServiceExecute(Sender: TService);
begin
  try
    CoInitialize(nil);
    Startup;
    try
      while not Terminated do begin
        DoSomeWork;
        ServiceThread.ProcessRequests(False);
      end;
    finally
      Cleanup;
      CoUninitialize;
    end;
  except
    on e: exception do begin
      PostLog('EXCEPTION in Execute: '+e.Message);
    end;
  end;
end;

//TryConnect might be called from same service thread and another thread
function TDBPool.TryConnect(const AConnStr: String): Bool;
var
  DB: TADOConnection; //Do I need CoInitialize in this function?
begin
  Result:= False;
  DB:= TADOConnection.Create(nil);
  try
    DB.LoginPrompt:= False;
    DB.ConnectionString:= AConnStr;
    try
      DB.Connected:= True;
      Result:= True;
    except
      on e: exception do begin
      end;
    end;
    DB.Connected:= False;
  finally
    DB.Free;
  end;
end;

因此,为了澄清它的确在做什么,我可能有这样的机会:

CoInitialize(nil);
try
  CoInitialize(nil);
  try
    //Do some ADO work
  finally
    CoUninitialize;
  end;
finally
  CoUninitialize;
end;

2 个答案:

答案 0 :(得分:14)

必须在使用COM的每个线程中调用

CoInitialize,无论它是什么线程,或者它是否具有父线程或子线程。如果线程使用COM,则必须调用CoInitialize

这里的正确答案是“它取决于”。由于您知道服务线程已调用CoInitialize,因此如果从服务线程调用TryConnect,则不需要再次调用它。如果可以调用它的其他线程也调用CoInitialize,则不需要调用它,因为该函数将在调用线程下运行。

MSDN文档专门针对这个问题(强调增加):

  

通常,COM库仅在线程上初始化一次。 在同一个线程上对CoInitialize或CoInitializeEx的后续调用将成功,只要它们不尝试更改并发模型,但将返回S_FALSE。要正常关闭COM库,每次成功调用CoInitialize或CoInitializeEx(包括返回S_FALSE的调用)都必须通过对CoUninitialize的相应调用进行平衡。但是,应用程序中第一个调用CoInitialize的线程为0(或者CoInitializeEx与COINIT_APARTMENTTHREADED)必须是调用CoUninitialize的最后一个线程。否则,后续对STA上的CoInitialize的调用将失败,应用程序将无法工作。

答案是:如果您不确定,请致电CoInitialize。在try..finally块中执行此操作,并在CoUnitialize中调用finally,或在构造函数中初始化并在析构函数中取消初始化。

答案 1 :(得分:4)

您的服务主管根本不应该做任何工作。它应该专门用于响应Service Manager调用。服务的OnExecute或OnStart / OnStop应控制实例化和执行代表服务功能的“MainWorkThread”。有关示例,请参阅https://stackoverflow.com/a/5748495/11225

主要工作线程可以完成实际工作和/或将其委托给其他线程。每个可以使用COM的线程都应该具有CoInitialize / CoUninitialize调用,最简单的方法是在线程的(重写)Execute方法的最外层try finally块中对它们进行编码。

使用COM的TDBPool或任何其他类不应该关注CoInitialize和CoUninitialize调用。需要在每个可以使用COM的线程中调用这些方法,并且类不会也不应该知道它将在哪个线程中执行。