multithreading 在这种情况下,我什么时候需要调用 CoInitialize()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9286600/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When do I need to call CoInitialize() in this scenario?
提问by Jerry Dodge
I'm building a multi-threaded windows service application in Delphi XE2 which uses ADO database components to connect to SQL Server. I've used CoInitialize(nil);
plenty times before inside threads, but in this case, I have a function which I'm unsure about.
我正在 Delphi XE2 中构建一个多线程 Windows 服务应用程序,它使用 ADO 数据库组件连接到 SQL Server。我CoInitialize(nil);
在内部线程之前使用过很多次,但在这种情况下,我有一个我不确定的函数。
This function is called TryConnect
which attempts to connect to a database with a given connection string. It returns true or false on the connection success. The problem is that this function will be used both inside and outside the main service thread, and it will be creating its own temporary TADOConnection
component, which requires CoInitialize
...
调用此函数会TryConnect
尝试使用给定的连接字符串连接到数据库。它在连接成功时返回 true 或 false。问题是这个函数会在主服务线程内部和外部使用,并且会创建自己的临时TADOConnection
组件,这需要CoInitialize
......
My question is do I need to call CoInitialize
inside this function also? If I do, and since the service's execute procedure uses CoInitialize
also, will they interfere if I call this function from within the service? The TryConnect
function is inside of an object which is created from the main service thread (but will eventually be moved to its own thread). I need to know if calling CoInitialize()
twice from the same thread (and CoUninitialize
) will interfere - and how to handle this scenario properly.
我的问题是我还需要CoInitialize
在这个函数内部调用吗?如果我这样做,并且由于服务的执行过程CoInitialize
也使用,如果我从服务内部调用此函数,它们会干扰吗?该TryConnect
函数位于从主服务线程创建的对象内部(但最终将移动到其自己的线程)。我需要知道CoInitialize()
从同一个线程(和CoUninitialize
)调用两次是否会干扰 - 以及如何正确处理这种情况。
Here's the code below...
这是下面的代码...
//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;
So to clarify what it's really doing, I might have an occasion of this:
所以为了澄清它真正在做什么,我可能有这样的机会:
CoInitialize(nil);
try
CoInitialize(nil);
try
//Do some ADO work
finally
CoUninitialize;
end;
finally
CoUninitialize;
end;
回答by Ken White
CoInitialize
has to be called in every single thread that uses COM, regardless of what thread it is, or whether it has a parent thread or child threads. If the thread uses COM, it must call CoInitialize
.
CoInitialize
必须在每个使用 COM 的线程中调用,无论它是什么线程,或者它是否有父线程或子线程。如果线程使用 COM,它必须调用CoInitialize
.
The correct answer here is "it depends". Since you know the service thread has called CoInitialize
, if TryConnect
is called from the service thread it won't need to be called again. If the other threads that could call it have also called CoInitialize
, it won't need to be called, as the function will run under the calling thread.
这里的正确答案是“视情况而定”。由于您知道服务线程已调用CoInitialize
,如果TryConnect
是从服务线程调用的,则不需要再次调用。如果可以调用它的其他线程也调用了CoInitialize
,则不需要调用它,因为该函数将在调用线程下运行。
The MSDNdocumentation specifically addresses this question (emphasis added):
在MSDN文档专门解决这个问题(强调):
Typically, the COM library is initialized on a thread only once. Subsequent calls to CoInitialize or CoInitializeEx on the same thread will succeed, as long as they do not attempt to change the concurrency model, but will return S_FALSE. To close the COM library gracefully, each successful call to CoInitialize or CoInitializeEx, including those that return S_FALSE, must be balanced by a corresponding call to CoUninitialize.However, the first thread in the application that calls CoInitialize with 0 (or CoInitializeEx with COINIT_APARTMENTTHREADED) must be the last thread to call CoUninitialize. Otherwise, subsequent calls to CoInitialize on the STA will fail and the application will not work.
通常,COM 库仅在线程上初始化一次。在同一线程上对 CoInitialize 或 CoInitializeEx 的后续调用将成功,只要它们不尝试更改并发模型,但将返回 S_FALSE。为了优雅地关闭 COM 库,对 CoInitialize 或 CoInitializeEx 的每次成功调用,包括那些返回 S_FALSE 的调用,都必须通过对 CoUninitialize 的相应调用来平衡。但是,应用程序中使用 0 调用 CoInitialize(或使用 COINIT_APARTMENTTHREADED 调用 CoInitializeEx)的第一个线程必须是最后一个调用 CoUninitialize 的线程。否则,在 STA 上对 CoInitialize 的后续调用将失败并且应用程序将无法工作。
So the answer is: If you're not sure, call CoInitialize
. Do it in a try..finally
block, and call CoUnitialize
in the finally
, or initialize in a constructor and uninitialize in the destructor.
所以答案是:如果您不确定,请致电CoInitialize
。做一个try..finally
块,和呼叫CoUnitialize
的finally
,或者在析构函数构造函数和未初始化初始化。
回答by Marjan Venema
Your service thread should really not be doing any work at all. It should be used exclusively for responding to Service Manager calls. The OnExecute or OnStart/OnStop of the service should control the instantiation and execution of a "MainWorkThread" that represents the functionality of your service. See https://stackoverflow.com/a/5748495/11225for an example.
您的服务线程根本不应该做任何工作。它应该专门用于响应服务管理器调用。服务的 OnExecute 或 OnStart/OnStop 应控制代表服务功能的“MainWorkThread”的实例化和执行。有关示例,请参见https://stackoverflow.com/a/5748495/11225。
The main work thread could do the real work and/or delegate it to other threads. Each thread that could use COM should have the CoInitialize/CoUninitialize calls and the easiest way to achieve that is to code them in the outer most try finally block of the thread's (overridden) Execute method.
主工作线程可以完成真正的工作和/或将其委托给其他线程。每个可以使用 COM 的线程都应该有 CoInitialize/CoUninitialize 调用,实现这一点的最简单方法是在线程的(覆盖的)Execute 方法的最外层 try finally 块中对它们进行编码。
TDBPool or any other class using COM should not concern itself with the CoInitialize and CoUninitialize calls. These methods need to be called in every thread that could use COM and a class doesn't and shouldn't know in which thread it will be executed.
TDBPool 或任何其他使用 COM 的类不应关注 CoInitialize 和 CoUninitialize 调用。这些方法需要在每个可以使用 COM 的线程中调用,而一个类不知道也不应该知道它将在哪个线程中执行。