windows C++-在启用 COM 的应用程序中使用并发(一)
也可以在使用组件对象模型 (COM) 的应用程序中使用并发运行时,关于COM组件的问题不在本文中讲述,默认读者理解com技术。
管理 COM 库的生存期
尽管将 COM 与并发运行时配合使用需遵循与任何其他并发机制相同的原则,不过以下准则可帮助你有效地一起使用这些库。
- 线程在使用 COM 库之前必须调用 CoInitializeEx;
- 只要线程为每个调用提供相同的参数,线程便可以多次调用 CoInitializeEx;
- 对于每个 CoInitializeEx 调用,线程还必须调用 CoUninitialize。 换句话说,对 CoInitializeEx 和 CoUninitialize 的调用必须均衡;
- 若要从一个线程单元切换到另一个线程单元,线程必须在使用新线程规范调用 CoInitializeEx 之前完全释放 COM 库;
将 COM 与并发运行时配合使用时,其他 COM 原则适用。 例如,在单线程单元 (STA) 中创建对象并将该对象封送到另一个单元的应用程序还必须提供消息循环来处理传入消息。 另请记住,在单元之间封送对象可能会降低性能。
将 COM 与并行模式库配合使用
将 COM 与并行模式库 (PPL) 中的组件(例如任务组或并行算法)一起使用时,需在每个任务或迭代期间使用 COM 库之前调用 CoInitializeEx,并在每个任务或迭代完成之前调用 CoUninitialize。 以下示例演示如何使用 concurrency::structured_task_group 对象管理 COM 库的生存期。
structured_task_group tasks;
// Create and run a task.
auto task = make_task([] {
// Initialize the COM library on the current thread.
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// TODO: Perform task here.
// Free the COM library.
CoUninitialize();
});
tasks.run(task);
// TODO: Run additional tasks here.
// Wait for the tasks to finish.
tasks.wait();
必须确保在取消任务或并行算法时或是任务体引发异常时正确释放 COM 库。 若要保证任务在退出前调用 CoUninitialize,请使用 try-finally 块或“资源获取即初始化”(RAII) 模式。 以下示例使用 try-finally 块在任务完成或取消时或是引发异常时释放 COM 库。
structured_task_group tasks;
// Create and run a task.
auto task = make_task([] {
bool coinit = false;
__try {
// Initialize the COM library on the current thread.
CoInitializeEx(NULL, COINIT_MULTITHREADED);
coinit = true;
// TODO: Perform task here.
}
__finally {
// Free the COM library.
if (coinit)
CoUninitialize();
}
});
tasks.run(task);
// TODO: Run additional tasks here.
// Wait for the tasks to finish.
tasks.wait();
以下示例使用 RAII 模式定义 CCoInitializer 类,该类在给定范围内管理 COM 库的生存期。
// An exception-safe wrapper class that manages the lifetime
// of the COM library in a given scope.
class CCoInitializer
{
public:
explicit CCoInitializer(DWORD dwCoInit = COINIT_APARTMENTTHREADED)
: _coinitialized(false)
{
// Initialize the COM library on the current thread.
HRESULT hr = CoInitializeEx(NULL, dwCoInit);
if (SUCCEEDED(hr))
_coinitialized = true;
}
~CCoInitializer()
{
// Free the COM library.
if (_coinitialized)
CoUninitialize();
}
private:
// Flags whether COM was properly initialized.
bool _coinitialized;
// Hide copy constructor and assignment operator.
CCoInitializer(const CCoInitializer&);
CCoInitializer& operator=(const CCoInitializer&);
};
当任务退出时,可以使用 CCoInitializer 类自动释放 COM 库,如下所示。
structured_task_group tasks;
// Create and run a task.
auto task = make_task([] {
// Enable COM for the lifetime of the task.
CCoInitializer coinit(COINIT_MULTITHREADED);
// TODO: Perform task here.
// The CCoInitializer object frees the COM library
// when the task exits.
});
tasks.run(task);
// TODO: Run additional tasks here.
// Wait for the tasks to finish.
tasks.wait();
将 COM 与异步代理配合使用
将 COM 与异步代理配合使用时,请先调用 CoInitializeEx,然后在 concurrency::agent::run 方法中将 COM 库用于代理。 随后在 run 方法返回之前调用 CoUninitialize。 请勿在代理的构造函数或析构函数中使用 COM 管理例程,并且请勿替代 concurrency::agent::start 或 concurrency::agent::done 方法,因为这些方法是从与 run 方法不同的线程进行调用。
以下示例演示一个名为 CCoAgent 的基本代理类,用于在 run 方法中管理 COM 库。
class CCoAgent : public agent
{
protected:
void run()
{
// Initialize the COM library on the current thread.
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// TODO: Perform work here.
// Free the COM library.
CoUninitialize();
// Set the agent to the finished state.
done();
}
};
将 COM 与轻量级任务配合使用
任务计划程序文档介绍了并发运行时中轻量级任务的角色。 可以将 COM 与轻量级任务配合使用,如同在 Windows API 中传递给 CreateThread 函数的任何线程例程一样。 这在下面的示例中显示。
// A basic lightweight task that you schedule directly from a
// Scheduler or ScheduleGroup object.
void ThreadProc(void* data)
{
// Initialize the COM library on the current thread.
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// TODO: Perform work here.
// Free the COM library.
CoUninitialize();
}