Sdílet prostřednictvím


Návod: Použití Concurrency Runtime v aplikaci s podporou modelu COM

Tento dokument ukazuje, jak použít modul Runtime souběžnosti v aplikaci, která používá modelu COM (Component Object).

Požadavky

Před zahájením tohoto návodu si přečtěte následující dokumenty:

Další informace o modelu COM naleznete v tématu Modelu COM (Component Object).

Správa životního cyklu knihovny modelu COM

Ačkoli použití modelu COM s modulem Runtime souběžnosti následuje na stejných principech jako jiným mechanismem řízení souběžnosti, můžete následující pokyny těchto knihoven společně efektivně používat.

  • Podproces musí volat u funkce CoInitializeEx před použitím knihovny COM.

  • Podproces může volat CoInitializeEx několikrát, dokud poskytuje stejné argumenty pro každé volání.

  • Pro každé volání CoInitializeEx, musíte také zavolat vlákno CoUninitialize.Jinými slovy, volání CoInitializeEx a CoUninitialize musí být vyváženo.

  • Přepnout z jednoho podprocesu apartment na jinou, vlákno musí zcela zdarma COM knihovny před volá CoInitializeEx s novým threading specifikace.

Platí jiné zásady COM při použití modelu COM s modulem Runtime souběžnosti.Například aplikace, která vytvoří objekt apartment s jedním podprocesem (STA) a zařazuje tento objekt do jiného bytu musí také poskytnout smyčka pro zpracování zpráv pro zpracování příchozích zpráv.Nezapomeňte také zařazování objektů mezi apartmány může snížit výkon.

Používání modelu COM s knihovnou PPL

Při použití modelu COM s komponentou v paralelní vzorky knihovny (PPL), například skupinu úkolů nebo paralelního algoritmu volání CoInitializeEx před použitím knihovny modelu COM v průběhu každého úkolu nebo iterace a volání CoUninitialize před dokončením jednotlivé úkoly nebo iterace.Následující příklad ukazuje, jak spravovat životnost knihovny modelu COM s concurrency::structured_task_group objektu.

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();

Musíte knihovny modelu COM je úkol nebo paralelní algoritmus je zrušena nebo těla úkolu vyvolá výjimku správně uvolněna.Zaručit, že úloha volání CoUninitialize před jejím ukončení použít try-finally bloku nebo Inicializace je pořízení prostředků vzorek (RAII).V následujícím příkladu try-finally bloku uvolnit knihovny COM při dokončení úkolu nebo je zrušena nebo je vyvolána výjimka.

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();

Následující příklad používá vzorek RAII definovat CCoInitializer třída, která spravuje životnost knihovny modelu COM v daném oboru.

// 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 (FAILED(hr))
         throw 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&);
};

Lze použít CCoInitializer třídy automaticky uvolnit knihovny COM při ukončení úkolu, takto.

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();

Další informace o zrušení v modulu Runtime souběžnosti naleznete v tématu Zrušení v knihovně PPL.

Používání modelu COM s asynchronními agenty

Při použití modelu COM s asynchronní agenti, volání CoInitializeEx před použitím knihovny modelu COM v concurrency::agent::run metoda pro váš agent.Volejte CoUninitialize před run metoda vrátí.Nepoužívejte rutiny správy modelu COM v konstruktoru a destruktoru vaše zástupce a přepsat concurrency::agent::start nebo concurrency::agent:: v metod vzhledem k tomu, že tyto metody jsou volány z jiného podprocesu než run metody.

Následující příklad ukazuje základní agent třídy s názvem CCoAgent, který spravuje knihovnu COM v run metody.

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();
   }
};

Kompletní příklad je k dispozici dále v tomto návodu.

Používání modelu COM s prostými úlohami

Dokument Plánovač úloh (Concurrency Runtime) popisuje roli lehké úlohy v modulu Runtime souběžnosti.Můžete použít COM s lehký úkol, stejně jako s libovolný podproces rutiny, která předáte CreateThread funkci v rozhraní API systému Windows.Toto je znázorněno v následujícím příkladu.

// 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();
}

Příklad aplikace s podporou modelu COM

Ukážeme kompletní aplikaci COM povoleno, používá IScriptControl rozhraní ke spuštění skriptu, který vypočítá nth Fibonacciho číslo.V tomto příkladu nejprve volá skript z hlavního vlákna a potom pomocí PPL a zoonóz souběžně volat skript.

Zvažte následující podpůrná funkce, RunScriptProcedure, která volá proceduru IScriptControl objektu.

// Calls a procedure in an IScriptControl object. 
template<size_t ArgCount>
_variant_t RunScriptProcedure(IScriptControlPtr pScriptControl, 
   _bstr_t& procedureName, array<_variant_t, ArgCount>& arguments)
{
   // Create a 1-dimensional, 0-based safe array.
   SAFEARRAYBOUND rgsabound[]  = { ArgCount, 0 };
   CComSafeArray<VARIANT> sa(rgsabound, 1U);

   // Copy the arguments to the safe array.
   LONG lIndex = 0;
   for_each(begin(arguments), end(arguments), [&](_variant_t& arg) {
      HRESULT hr = sa.SetAt(lIndex, arg);
      if (FAILED(hr))
         throw hr;
      ++lIndex;
   });

   //  Call the procedure in the script. 
   return pScriptControl->Run(procedureName, &sa.m_psa);
}

wmain Funkce vytvoří IScriptControl objekt, kód skriptu přidá které počítá nth Fibonacciho číslo a pak zavolá RunScriptProcedure funkce pro spuštění tohoto skriptu.

int wmain()
{
   HRESULT hr;

   // Enable COM on this thread for the lifetime of the program.   
   CCoInitializer coinit(COINIT_MULTITHREADED);

   // Create the script control.
   IScriptControlPtr pScriptControl(__uuidof(ScriptControl));

   // Set script control properties.
   pScriptControl->Language = "JScript";
   pScriptControl->AllowUI = TRUE;

   // Add script code that computes the nth Fibonacci number.
   hr = pScriptControl->AddCode(
      "function fib(n) { if (n<2) return n; else return fib(n-1) + fib(n-2); }" );
   if (FAILED(hr))
      return hr;

   // Test the script control by computing the 15th Fibonacci number.
   wcout << endl << L"Main Thread:" << endl;
   LONG lValue = 15;
   array<_variant_t, 1> args = { _variant_t(lValue) };
   _variant_t result = RunScriptProcedure(
      pScriptControl, 
      _bstr_t("fib"), 
      args);
   // Print the result.
   wcout << L"fib(" << lValue << L") = " << result.lVal << endl;

   return S_OK;
}

Volání skriptu z knihovny PPL

Následující funkce ParallelFibonacci, používá concurrency::parallel_for algoritmu pro volání skriptu paralelně.Tato funkce využívá CCoInitializer třídy ke správě platnosti knihovny COM během každé opakování úkolu.

// Computes multiple Fibonacci numbers in parallel by using  
// the parallel_for algorithm.
HRESULT ParallelFibonacci(IScriptControlPtr pScriptControl)
{
   try {
      parallel_for(10L, 20L, [&pScriptControl](LONG lIndex) 
      {
         // Enable COM for the lifetime of the task.
         CCoInitializer coinit(COINIT_MULTITHREADED);

         // Call the helper function to run the script procedure. 
         array<_variant_t, 1> args = { _variant_t(lIndex) };
         _variant_t result = RunScriptProcedure(
            pScriptControl, 
            _bstr_t("fib"), 
            args);

         // Print the result.
         wstringstream ss;         
         ss << L"fib(" << lIndex << L") = " << result.lVal << endl;
         wcout << ss.str();
      });
   }
   catch (HRESULT hr) {
      return hr;
   }
   return S_OK;
}

Chcete-li použít ParallelFibonacci pracovat s příkladem, přidejte následující kód před wmain funkce vrátí.

// Use the parallel_for algorithm to compute multiple  
// Fibonacci numbers in parallel.
wcout << endl << L"Parallel Fibonacci:" << endl;
if (FAILED(hr = ParallelFibonacci(pScriptControl)))
   return hr;

Volání skriptu z agenta

Následující příklad ukazuje FibonacciScriptAgent třídy, které volá proceduru skriptu pro výpočet nth Fibonacciho číslo.FibonacciScriptAgent Používá třída zprávy a přijímat z hlavního programu Vstupní hodnoty funkce skriptu.run Metoda spravuje životnost knihovny modelu COM v průběhu úkolu.

// A basic agent that calls a script procedure to compute the  
// nth Fibonacci number. 
class FibonacciScriptAgent : public agent
{
public:
   FibonacciScriptAgent(IScriptControlPtr pScriptControl, ISource<LONG>& source)
      : _pScriptControl(pScriptControl)
      , _source(source) { }

public:
   // Retrieves the result code.
   HRESULT GetHRESULT() 
   {
      return receive(_result);
   }

protected:
   void run()
   {
      // Initialize the COM library on the current thread.
      CoInitializeEx(NULL, COINIT_MULTITHREADED);

      // Read values from the message buffer until  
      // we receive the sentinel value.      
      LONG lValue;
      while ((lValue = receive(_source)) != Sentinel)
      {
         try {
            // Call the helper function to run the script procedure. 
            array<_variant_t, 1> args = { _variant_t(lValue) };
            _variant_t result = RunScriptProcedure(
               _pScriptControl, 
               _bstr_t("fib"), 
               args);

            // Print the result.
            wstringstream ss;         
            ss << L"fib(" << lValue << L") = " << result.lVal << endl;
            wcout << ss.str();
         }
         catch (HRESULT hr) {
            send(_result, hr);
            break;    
         }
      }

      // Set the result code (does nothing if a value is already set).
      send(_result, S_OK);

      // Free the COM library.
      CoUninitialize();

      // Set the agent to the finished state.
      done();
   }

public:
   // Signals the agent to terminate. 
   static const LONG Sentinel = 0L;

private:
   // The IScriptControl object that contains the script procedure.
   IScriptControlPtr _pScriptControl;
   // Message buffer from which to read arguments to the  
   // script procedure.
   ISource<LONG>& _source;
   // The result code for the overall operation.
   single_assignment<HRESULT> _result;
};

Následující funkce AgentFibonacci, vytvoří několik FibonacciScriptAgent objekty a používá zprávy a odeslat několik vstupní hodnoty pro tyto objekty.

// Computes multiple Fibonacci numbers in parallel by using  
// asynchronous agents.
HRESULT AgentFibonacci(IScriptControlPtr pScriptControl)
{
   // Message buffer to hold arguments to the script procedure.
   unbounded_buffer<LONG> values;

   // Create several agents. 
   array<agent*, 3> agents = 
   {
      new FibonacciScriptAgent(pScriptControl, values),
      new FibonacciScriptAgent(pScriptControl, values),
      new FibonacciScriptAgent(pScriptControl, values),
   };

   // Start each agent.
   for_each(begin(agents), end(agents), [](agent* a) {
      a->start();
   });

   // Send a few values to the agents.
   send(values, 30L);
   send(values, 22L);
   send(values, 10L);
   send(values, 12L);
   // Send a sentinel value to each agent.
   for_each(begin(agents), end(agents), [&values](agent*) {
      send(values, FibonacciScriptAgent::Sentinel);
   });

   // Wait for all agents to finish.
   agent::wait_for_all(3, &agents[0]);

   // Determine the result code.
   HRESULT hr = S_OK;
   for_each(begin(agents), end(agents), [&hr](agent* a) {
      HRESULT hrTemp;
      if (FAILED(hrTemp = 
         reinterpret_cast<FibonacciScriptAgent*>(a)->GetHRESULT()))
      {
         hr = hrTemp;
      }
   });

   // Clean up.
   for_each(begin(agents), end(agents), [](agent* a) {
      delete a;
   });

   return hr;
}

Chcete-li použít AgentFibonacci pracovat s příkladem, přidejte následující kód před wmain funkce vrátí.

// Use asynchronous agents to compute multiple  
// Fibonacci numbers in parallel.
wcout << endl << L"Agent Fibonacci:" << endl;
if (FAILED(hr = AgentFibonacci(pScriptControl)))
   return hr;

Kompletní příklad

Následující kód ukazuje kompletní příklad, který používá paralelní algoritmy a asynchronní agenty k volání procedury skriptu, který vypočítá Fibonacciho čísla.

// parallel-scripts.cpp 
// compile with: /EHsc 

#include <agents.h>
#include <ppl.h>
#include <array>
#include <sstream>
#include <iostream>
#include <atlsafe.h>

// TODO: Change this path if necessary.
#import "C:\windows\system32\msscript.ocx" 

using namespace concurrency;
using namespace MSScriptControl;
using namespace std;

// 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 (FAILED(hr))
         throw 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&);
};

// Calls a procedure in an IScriptControl object. 
template<size_t ArgCount>
_variant_t RunScriptProcedure(IScriptControlPtr pScriptControl, 
   _bstr_t& procedureName, array<_variant_t, ArgCount>& arguments)
{
   // Create a 1-dimensional, 0-based safe array.
   SAFEARRAYBOUND rgsabound[]  = { ArgCount, 0 };
   CComSafeArray<VARIANT> sa(rgsabound, 1U);

   // Copy the arguments to the safe array.
   LONG lIndex = 0;
   for_each(begin(arguments), end(arguments), [&](_variant_t& arg) {
      HRESULT hr = sa.SetAt(lIndex, arg);
      if (FAILED(hr))
         throw hr;
      ++lIndex;
   });

   //  Call the procedure in the script. 
   return pScriptControl->Run(procedureName, &sa.m_psa);
}

// Computes multiple Fibonacci numbers in parallel by using  
// the parallel_for algorithm.
HRESULT ParallelFibonacci(IScriptControlPtr pScriptControl)
{
   try {
      parallel_for(10L, 20L, [&pScriptControl](LONG lIndex) 
      {
         // Enable COM for the lifetime of the task.
         CCoInitializer coinit(COINIT_MULTITHREADED);

         // Call the helper function to run the script procedure. 
         array<_variant_t, 1> args = { _variant_t(lIndex) };
         _variant_t result = RunScriptProcedure(
            pScriptControl, 
            _bstr_t("fib"), 
            args);

         // Print the result.
         wstringstream ss;         
         ss << L"fib(" << lIndex << L") = " << result.lVal << endl;
         wcout << ss.str();
      });
   }
   catch (HRESULT hr) {
      return hr;
   }
   return S_OK;
}

// A basic agent that calls a script procedure to compute the  
// nth Fibonacci number. 
class FibonacciScriptAgent : public agent
{
public:
   FibonacciScriptAgent(IScriptControlPtr pScriptControl, ISource<LONG>& source)
      : _pScriptControl(pScriptControl)
      , _source(source) { }

public:
   // Retrieves the result code.
   HRESULT GetHRESULT() 
   {
      return receive(_result);
   }

protected:
   void run()
   {
      // Initialize the COM library on the current thread.
      CoInitializeEx(NULL, COINIT_MULTITHREADED);

      // Read values from the message buffer until  
      // we receive the sentinel value.      
      LONG lValue;
      while ((lValue = receive(_source)) != Sentinel)
      {
         try {
            // Call the helper function to run the script procedure. 
            array<_variant_t, 1> args = { _variant_t(lValue) };
            _variant_t result = RunScriptProcedure(
               _pScriptControl, 
               _bstr_t("fib"), 
               args);

            // Print the result.
            wstringstream ss;         
            ss << L"fib(" << lValue << L") = " << result.lVal << endl;
            wcout << ss.str();
         }
         catch (HRESULT hr) {
            send(_result, hr);
            break;    
         }
      }

      // Set the result code (does nothing if a value is already set).
      send(_result, S_OK);

      // Free the COM library.
      CoUninitialize();

      // Set the agent to the finished state.
      done();
   }

public:
   // Signals the agent to terminate. 
   static const LONG Sentinel = 0L;

private:
   // The IScriptControl object that contains the script procedure.
   IScriptControlPtr _pScriptControl;
   // Message buffer from which to read arguments to the  
   // script procedure.
   ISource<LONG>& _source;
   // The result code for the overall operation.
   single_assignment<HRESULT> _result;
};

// Computes multiple Fibonacci numbers in parallel by using  
// asynchronous agents.
HRESULT AgentFibonacci(IScriptControlPtr pScriptControl)
{
   // Message buffer to hold arguments to the script procedure.
   unbounded_buffer<LONG> values;

   // Create several agents. 
   array<agent*, 3> agents = 
   {
      new FibonacciScriptAgent(pScriptControl, values),
      new FibonacciScriptAgent(pScriptControl, values),
      new FibonacciScriptAgent(pScriptControl, values),
   };

   // Start each agent.
   for_each(begin(agents), end(agents), [](agent* a) {
      a->start();
   });

   // Send a few values to the agents.
   send(values, 30L);
   send(values, 22L);
   send(values, 10L);
   send(values, 12L);
   // Send a sentinel value to each agent.
   for_each(begin(agents), end(agents), [&values](agent*) {
      send(values, FibonacciScriptAgent::Sentinel);
   });

   // Wait for all agents to finish.
   agent::wait_for_all(3, &agents[0]);

   // Determine the result code.
   HRESULT hr = S_OK;
   for_each(begin(agents), end(agents), [&hr](agent* a) {
      HRESULT hrTemp;
      if (FAILED(hrTemp = 
         reinterpret_cast<FibonacciScriptAgent*>(a)->GetHRESULT()))
      {
         hr = hrTemp;
      }
   });

   // Clean up.
   for_each(begin(agents), end(agents), [](agent* a) {
      delete a;
   });

   return hr;
}

int wmain()
{
   HRESULT hr;

   // Enable COM on this thread for the lifetime of the program.   
   CCoInitializer coinit(COINIT_MULTITHREADED);

   // Create the script control.
   IScriptControlPtr pScriptControl(__uuidof(ScriptControl));

   // Set script control properties.
   pScriptControl->Language = "JScript";
   pScriptControl->AllowUI = TRUE;

   // Add script code that computes the nth Fibonacci number.
   hr = pScriptControl->AddCode(
      "function fib(n) { if (n<2) return n; else return fib(n-1) + fib(n-2); }" );
   if (FAILED(hr))
      return hr;

   // Test the script control by computing the 15th Fibonacci number.
   wcout << L"Main Thread:" << endl;
   long n = 15;
   array<_variant_t, 1> args = { _variant_t(n) };
   _variant_t result = RunScriptProcedure(
      pScriptControl, 
      _bstr_t("fib"), 
      args);
   // Print the result.
   wcout << L"fib(" << n << L") = " << result.lVal << endl;

   // Use the parallel_for algorithm to compute multiple  
   // Fibonacci numbers in parallel.
   wcout << endl << L"Parallel Fibonacci:" << endl;
   if (FAILED(hr = ParallelFibonacci(pScriptControl)))
      return hr;

   // Use asynchronous agents to compute multiple  
   // Fibonacci numbers in parallel.
   wcout << endl << L"Agent Fibonacci:" << endl;
   if (FAILED(hr = AgentFibonacci(pScriptControl)))
      return hr;

   return S_OK;
}

V příkladu vytvoří následující výstup.

  

Probíhá kompilace kódu

Zkopírovat ukázkový kód a vložit jej do projektu sady Visual Studio nebo vložit do souboru s názvem paralelní scripts.cpp a potom spusťte následující příkaz v okně Příkazový řádek Visual Studio.

cl.exe /EHsc parallel-scripts.cpp /link ole32.lib

Viz také

Koncepty

Funkční paralelismus (Concurrency Runtime)

Paralelní algoritmy

Asynchronní agenti

Zpracování výjimek v Concurrency Runtime

Zrušení v knihovně PPL

Plánovač úloh (Concurrency Runtime)

Další zdroje

Návody k Concurrency Runtime