İzlenecek yol: COM Özellikli bir Uygulamada Eşzamanlılık Çalışma Zamanını Kullanma

Bu belge eşzamanlılık çalışma zamanı Bileşen Nesne Modeli (COM) kullanan bir uygulama içinde kullanımı gösterilmiştir.

Önkoşullar

Bu gözden geçirmeye başlamadan önce aşağıdaki belgeleri okuyun:

COM hakkında daha fazla bilgi için bkz: Bileşen Nesne Modeli (COM).

COM Kitaplığının Kullanım Süresini Yönetme

Herhangi bir eşzamanlılık mekanizma aynı ilkeleri COM eşzamanlılık çalışma zamanı ile kullanımını izleyen olsa da, aşağıdaki yönergeler bu kitaplıklar birlikte etkili biçimde kullanmak yardımcı olabilir.

  • Bir iş parçacığı çağırmalısınız CoInitializeEx önce COM kitaplığını kullanır.

  • Bir iş parçacığı çağırabilir CoInitializeEx birden çok kez aynı bağımsız değişkenler her çağrı için sağladığı sürece.

  • Her çağrı için CoInitializeEx, bir iş parçacığı ayrıca çağırmalısınız CoUninitialize.Başka bir deyişle, çağrılar CoInitializeEx ve CoUninitialize dengelenmelidir.

  • Onu çağırmadan önce tek iş parçacığı grubu diğerine geçmek için bir iş parçacığı tamamen COM kitaplığını boşaltmalısınız CoInitializeEx yeni iş parçacığı belirtimi.

Eşzamanlılık Çalışma zamanı ile COM kullandığınızda diğer COM ilkeler uygulanır.Örneğin, bir tek iş parçacıklı grup (STA) bir nesne oluşturur ve bu nesne için başka bir grup sıralar bir uygulama ileti döngüsü gelen iletileri işlemek için de sağlaması gerekir.Ayrıca grupların arasında nesneleri hazırlama performansını düşürebilir unutmayın.

COM'u Paralel Desenler Kitaplığıyla Kullanma

COM bileşeni içinde paralel Desen kitaplığı (PPL), örneğin, bir görev grubu veya paralel algoritma ile kullandığınızda, arama CoInitializeEx her görev veya yineleme ve çağrı sırasında COM kitaplığını kullanmadan önce CoUninitialize her görev veya yineleme tamamlanmadan önce.Aşağıdaki örnek COM kitaplığı ile yaşam yönetmek nasıl gösterir bir concurrency::structured_task_group nesne.

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 kitaplığı düzgün bir görev veya paralel algoritma iptal edildiğinde veya görev gövde özel durum oluşturduğunda bırakıldığını emin olmanız gerekir.Görev güvence altına almak için çağırır CoUninitialize onu çıkar önce kullanmak bir try-finally blok veya Olan kaynak alma başlatma (RAII) deseni.Aşağıdaki örnek bir try-finally COM kitaplığı görevi tamamlar veya iptal edilene veya bir özel durum zaman boşaltmak için blok.

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

Aşağıdaki örnek RAII deseni tanımlamak için kullanır CCoInitializer sınıfı, COM kitaplığı için belirli bir kapsamda yaşam yönetir.

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

Kullanabileceğiniz CCoInitializer COM kitaplığı görev, aşağıdaki gibi çıktığında otomatik olarak boşaltmak için sınıf.

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

Eşzamanlılık Çalışma zamanı içinde iptal etme hakkında daha fazla bilgi için bkz: PPL'de İptal.

COM'u Zaman Uyumsuz Aracılarla Kullanma

Zaman uyumsuz aracılar ile COM kullandığınızda, arama CoInitializeEx COM kitaplığı'nda kullanmadan önce concurrency::agent::run aracınız için yöntem.Sonra arama CoUninitialize önce run yöntemini döndürür.COM yönetim yordamları yapıcı veya yıkıcı, Aracısı kullanmayan ve geçersiz concurrency::agent::start veya concurrency::agent:: yapılan yöntemleri bu yöntemlerin farklı bir iş parçacığı tarafından denir çünkü run yöntem.

Aşağıdaki örnek adında bir temel aracı sınıfı gösterir CCoAgent, COM kitaplığını yöneten run yöntemi.

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

Bu izlenecek yolda, tam bir örnek sağlanmıştır.

COM'u Basit Görevlerle Kullanma

Belgeyi Görev Zamanlayıcı (Eşzamanlılık Çalışma Zamanı) eşzamanlılık çalışma zamanı basit görevleri açıklar.Geçişi için herhangi bir iş parçacığı yordamı ile gibi basit bir görev ile COM kullanabilirsiniz CreateThread Windows API işlev.Bu, aşağıdaki örnekte gösterilir.

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

COM Özellikli Uygulama Örneği

Bu bölüm kullanan tam bir COM etkin uygulamanın gösterir IScriptControl n hesaplayan bir komut dosyasını yürütmek için arabirimth Fibonacci numarası.Bu örnek ilk komut dosyası ana iş parçacığından çağırır ve sonra komut dosyasını aynı anda aramak için PPL ve aracıları kullanır.

Aşağıdaki, yardımcı bir işlev, göz önünde RunScriptProcedure, hangi çağıran bir yordam bir IScriptControl nesne.

// 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 İşlevi oluşturur bir IScriptControl nesne, komut dosyası kodunu hesaplayan n eklerth Fibonacci numarası ve çağrı RunScriptProcedure bu komut dosyasını çalıştırmak için işlev.

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

PPL'den Betik çağırma

Aşağıdaki işlev, ParallelFibonacci, kullanan concurrency::parallel_for paralel olarak komut dosyasını çağırmak için algoritma.Bu işlev kullanır CCoInitializer sınıfı, COM kitaplığını yaşam her görevin yineleme boyunca yönetmek için.

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

Kullanmak için ParallelFibonacci ile birlikte örnek işlev, önce aşağıdaki kodu ekleyin wmain işlevini verir.

// 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;

Bir Aracıdan Betik çağırma

Aşağıdaki örnekte gösterildiği FibonacciScriptAgent n hesaplamak için bir komut dosyası yordamı çağırır sınıfth Fibonacci numarası.FibonacciScriptAgent Sınıfı, ileti geçirme, ana programdan almak için giriş değerleri komut dosyası işlevini kullanır.run Yöntemi yaşam süresi boyunca görev COM kitaplığı yönetir.

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

Aşağıdaki işlev, AgentFibonacci, birkaç oluşturur FibonacciScriptAgent nesne ve ileti geçirmenin birkaç göndermek için giriş değerleri bu nesneleri için kullanır.

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

Kullanmak için AgentFibonacci ile birlikte örnek işlev, önce aşağıdaki kodu ekleyin wmain işlevini verir.

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

Tam Örnek

Aşağıdaki kod, paralel algoritmalar ve zaman uyumsuz aracılar Fibonacci sayıları hesaplayan bir komut dosyası yordamı çağırmak için kullandığı tam bir örnek gösterilir.

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

Örneğin, aşağıdaki örnek çıktı oluşturur.

  

Kod Derleniyor

Örnek kodu kopyalayın ve Visual Studio projesinde yapıştırın veya adlı bir dosyaya yapıştırın paralel scripts.cpp ve Visual Studio komut istemi penceresinde aşağıdaki komutu çalıştırın.

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

Ayrıca bkz.

Kavramlar

Görev Parallelliği (Eşzamanlılık Çalışma Zamanı)

Paralel Algoritmalar

Zaman Uyumsuz Aracılar

Eşzamanlılık Çalışma Zamanında Özel Durum İşleme

PPL'de İptal

Görev Zamanlayıcı (Eşzamanlılık Çalışma Zamanı)

Diğer Kaynaklar

Eşzamanlılık Çalışma Zamanı İzlenecek Yollar