Aracılığıyla paylaş


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

Bu belge, Bileşen Nesne Modeli (COM) kullanan bir uygulamada Eşzamanlılık Çalışma Zamanı'nın nasıl kullanılacağını gösterir.

Ön koşullar

Bu izlenecek yolu başlatmadan ö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

COM'un Eşzamanlılık Çalışma Zamanı ile kullanımı diğer tüm eşzamanlılık mekanizmalarıyla aynı ilkelere uysa da, aşağıdaki yönergeler bu kitaplıkları birlikte etkili bir şekilde kullanmanıza yardımcı olabilir.

  • İş parçacığıNıN COM kitaplığını kullanmadan önce CoInitializeEx'i çağırması gerekir.

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

  • her çağrısı için CoInitializeExbir iş parçacığının da CoUninitialize çağrısı yapması gerekir. Başka bir deyişle ve çağrıları CoInitializeExCoUninitialize dengelenmelidir.

  • Bir iş parçacığı dairesinden diğerine geçmek için, iş parçacığının yeni iş parçacığı belirtimi ile çağırmadan CoInitializeEx önce COM kitaplığını tamamen boşaltması gerekir.

Eşzamanlılık Çalışma Zamanı ile COM kullandığınızda diğer COM ilkeleri geçerlidir. Örneğin, tek iş parçacıklı bir dairede (STA) bir nesne oluşturan ve bu nesneyi başka bir daireye sıralayan bir uygulama, gelen iletileri işlemek için bir ileti döngüsü de sağlamalıdır. Ayrıca, daireler arasında nesneleri hazırlamanın performansı düşürebileceğini unutmayın.

COM'u Paralel Desenler Kitaplığıyla Kullanma

Bir görev grubu veya paralel algoritma gibi Paralel Desenler Kitaplığı'ndaki (PPL) bir bileşenle COM kullandığınızda, her görev veya yineleme sırasında COM kitaplığını kullanmadan önce çağırın CoInitializeEx ve her görev veya yineleme tamamlanmadan önce çağırın CoUninitialize . Aşağıdaki örnekte, com kitaplığının yaşam ömrünün eşzamanlılık ::structured_task_group nesnesiyle nasıl yönetileceğini gösterir.

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

Bir görev veya paralel algoritma iptal edildiğinde veya görev gövdesi özel durum oluştururken COM kitaplığının doğru şekilde serbest olduğundan emin olmanız gerekir. Görevin çıkmadan önce çağırdığını CoUninitialize garanti etmek için bir try-finally blok veya Kaynak Alımı Başlatma (RAII) deseni kullanın. Aşağıdaki örnek, görev tamamlandığında veya iptal edildiğinde ya da bir özel durum oluştuğunda COM kitaplığını boşaltmak için bir try-finally blok kullanır.

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, belirli bir kapsamdaKI COM kitaplığının CCoInitializer ömrünü yöneten sınıfını tanımlamak için RAII desenini kullanır.

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

Görevden çıkıldığında COM kitaplığını aşağıdaki gibi otomatik olarak boşaltmak için sınıfını kullanabilirsiniz CCoInitializer .

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ı'nda iptal hakkında daha fazla bilgi için bkz . PPL'de İptal.

COM'u Zaman Uyumsuz Aracılarla Kullanma

COM'u zaman uyumsuz aracılarla kullandığınızda, aracınız için concurrency::agent::run yönteminde COM kitaplığını kullanmadan önce çağrısı CoInitializeEx yapın. Ardından yöntemi döndürmeden önce run çağrısı CoUninitialize yapın. Aracınızın oluşturucusunda veya yok edicisinde COM yönetim yordamlarını kullanmayın ve concurrency::agent::start veya concurrency::agent::d one yöntemlerini geçersiz kılmayın çünkü bu yöntemler yöntemden run farklı bir iş parçacığından çağrılır.

Aşağıdaki örnekte, yöntemindeki COM kitaplığını yöneten adlı CCoAgenttemel bir aracı sınıfı gösterilmektedir run .

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 kılavuzun ilerleyen bölümlerinde eksiksiz bir örnek verilmiştir.

COM'u Basit Görevlerle Kullanma

Belge Görev Zamanlayıcı , Eşzamanlılık Çalışma Zamanı'ndaki basit görevlerin rolünü açıklar. WINDOWS API'sindeki işleve CreateThread geçirdiğiniz herhangi bir iş parçacığı yordamında olduğu gibi COM'yi basit bir görevle kullanabilirsiniz. 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ümde, n. Fibonacci numarasını hesaplayan bir betik yürütmek için arabirimini kullanan IScriptControl com özellikli eksiksiz bir uygulama gösterilmektedir. Bu örnek önce ana iş parçacığından betiği çağırır ve ardından betiği eşzamanlı olarak çağırmak için PPL ve aracıları kullanır.

Bir nesnedeki IScriptControl yordamı çağıran aşağıdaki yardımcı işlevini RunScriptProceduregöz önünde bulundurun.

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

İşlev wmain bir IScriptControl nesne oluşturur, ona n. Fibonacci numarasını hesaplayan betik kodu ekler ve ardından bu betiği çalıştırmak için işlevini çağırır RunScriptProcedure .

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şlevi, ParallelFibonaccibetiği paralel olarak çağırmak için concurrency::p arallel_for algoritmasını kullanır. Bu işlev, görevin her yinelemesi sırasında COM kitaplığının ömrünü yönetmek için sınıfını kullanır CCoInitializer .

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

İşlevi ParallelFibonacci örnekle birlikte kullanmak için işlev döndürmeden önce wmain aşağıdaki kodu ekleyin.

// 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 örnek, n. Fibonacci numarasını hesaplamak için bir betik yordamı çağıran sınıfını gösterirFibonacciScriptAgent. sınıfı FibonacciScriptAgent , ana programdan betik işlevine giriş değerlerini almak için ileti geçirmeyi kullanır. run yöntemi, görev boyunca COM kitaplığının ömrünü 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şlevi, AgentFibonaccibirkaç FibonacciScriptAgent nesne oluşturur ve ileti geçirmeyi kullanarak bu nesnelere birkaç giriş değeri gönderir.

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

İşlevi AgentFibonacci örnekle birlikte kullanmak için işlev döndürmeden önce wmain aşağıdaki kodu ekleyin.

// 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, Fibonacci numaralarını hesaplayan bir betik yordamı çağırmak için paralel algoritmalar ve zaman uyumsuz aracılar kullanan tam örneği gösterir.

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

Örnek aşağıdaki örnek çıktıyı oluşturur.

Main Thread:
fib(15) = 610

Parallel Fibonacci:
fib(15) = 610
fib(10) = 55
fib(16) = 987
fib(18) = 2584
fib(11) = 89
fib(17) = 1597
fib(19) = 4181
fib(12) = 144
fib(13) = 233
fib(14) = 377

Agent Fibonacci:
fib(30) = 832040
fib(22) = 17711
fib(10) = 55
fib(12) = 144

Kod Derleniyor

Örnek kodu kopyalayıp bir Visual Studio projesine yapıştırın veya adlı parallel-scripts.cpp bir dosyaya yapıştırın ve ardından bir Visual Studio Komut İstemi penceresinde aşağıdaki komutu çalıştırın.

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

Ayrıca bkz.

Eşzamanlılık Çalışma Zamanı İzlenecek Yollar
Görev Paralelliği
Paralel Algoritmalar
Zaman Uyumsuz Aracılar
Özel Durum İşleme
PPL'de İptal
Görev Zamanlayıcı