Wskazówki: korzystanie ze współbieżności środowiska wykonawczego w aplikacji z możliwością korzystania z COM
W tym dokumencie przedstawiono sposoby używania środowiska wykonawczego współbieżności w aplikacji korzystającej z zestawu preinstalacji OEM (COM, Component Object Model).
Wymagania wstępne
Przed rozpoczęciem tego instruktażu, przeczytaj następujące dokumenty:
Aby uzyskać więcej informacji dotyczących modelu COM, zobacz Hosta skryptów systemu Windows (COM, Component Object Model).
Zarządzanie okresem istnienia biblioteki COM
Mimo że wykorzystanie COM wraz ze środowiskiem wykonawczym współbieżność podlega tym samym zasadom, co innego mechanizmu współbieżności, następujących wytycznych może pomóc w tych bibliotek razem efektywnie korzystać.
Wątek musi wywołać metodę funkcja CoInitializeEx przed użyciem biblioteki COM.
Wątek może wywołać CoInitializeEx wiele razy, jak długo zapewnia te same argumenty do każdego wywołania.
Dla każdego wywołania CoInitializeEx, wątek musi także wywołać metodę CoUninitialize.Innymi słowy, wzywa do CoInitializeEx i CoUninitialize musi być zrównoważony.
Aby przełączyć się z jednego wątku apartament, wątek musi całkowicie zwolnić biblioteki COM przed wywołuje CoInitializeEx z nowym threading specyfikacji.
Przy użyciu modelu COM z Runtime współbieżność obowiązują inne zasady COM.Na przykład aplikacja, która tworzy obiekt w jednowątkowym apartamencie (STA) i marszałków tego obiektu do innego mieszkania musi także udostępnić pętli komunikatów do przetwarzania komunikatów przychodzących.Należy również pamiętać, że kierowanie obiektów między apartamenty mogą spowalniać działanie.
Korzystając z modelu COM z biblioteką równoległych wzorców
Podczas korzystania ze składnika w równoległych wzorców biblioteki (PPL), na przykład grupy zadań lub algorytm równoległy COM wywołać CoInitializeEx przed użyciem biblioteki COM podczas każdego zadania lub iteracji i wywołanie CoUninitialize przed zakończeniem każdego zadania lub iteracji.Poniższy przykład pokazuje, jak zarządzać okresem istnienia biblioteki COM z concurrency::structured_task_group obiektu.
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();
Należy się upewnić, biblioteki COM poprawnie jest zwalniane po anulowaniu zadania lub równoległy algorytm lub gdy treści zadania zgłasza wyjątek.Aby zagwarantować, że zadanie wywołuje CoUninitialize przed jego wyjściu, użyj try-finally bloku lub Inicjowania jest przejęcie zasobu wzór (RAII).W poniższym przykładzie użyto try-finally bloku do swobodnego biblioteki COM, gdy zadanie zakończy się lub zostało anulowane, lub gdy wyjątek.
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();
W poniższym przykładzie użyto do definiowania wzór RAII CCoInitializer klasy, która zarządza istnienia biblioteki COM w danym zakresie.
// 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&);
};
Można użyć CCoInitializer klasy, aby automatycznie zwolnić biblioteki COM podczas zadania kończy pracę w następujący sposób.
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();
Aby uzyskać więcej informacji na temat anulowania w czasie wykonywania współbieżność zobacz Anulowanie w PPL.
Korzystając z modelu COM z agentami asynchronicznymi
Podczas korzystania z modelu COM z agentami asynchronicznych, wywołać CoInitializeEx przed użyciem biblioteki COM w concurrency::agent::run metody dla swojego agenta.Następnie wywołać CoUninitialize przed run metoda zwraca.Nie należy używać procedur zarządzania COM w konstruktorze lub destruktora swojego agenta, a nie zastępują concurrency::agent::start lub concurrency::agent:: Sporządzono metod, ponieważ metody te są nazywane z wątku innego niż run metody.
Poniższy przykład pokazuje klasę podstawowe agenta, o nazwie CCoAgent, która zarządza biblioteki COM w 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();
}
};
Rozbudowany przykład znajduje się w dalszej części tego instruktażu.
Korzystając z modelu COM z lekkimi zadaniami
Dokument Harmonogram zadań (współbieżność środowiska wykonawczego) opisuje rolę lekki zadania w czasie wykonywania współbieżności.Można użyć COM z zadaniem lekki, tak jak wszelkie rutynowe wątku, który jest przekazywany do CreateThread funkcja w interfejsie API systemu Windows.W poniższym przykładzie jest to pokazane.
// 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();
}
Przykład aplikacji z możliwością korzystania z COM
W tej sekcji przedstawiono pełną aplikacji obsługującej na modelu COM, która używa IScriptControl interfejsu, aby wykonać skrypt, który oblicza nth fibonacciego.W tym przykładzie najpierw wywołuje skrypt z wątku głównego, a następnie używa jednocześnie wywołać skrypt PPL i agentów.
Należy wziąć pod uwagę następujące funkcja helper, RunScriptProcedure, która wywołuje procedurę IScriptControl obiektu.
// 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 Funkcja tworzy IScriptControl obiektów, dodaje kod skryptu do niej, który oblicza nth fibonacciego, a następnie wywołania RunScriptProcedure funkcji do uruchomienia tego skryptu.
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;
}
Wywoływanie skryptu z PPL
Poniższa funkcja ParallelFibonacci, korzysta z concurrency::parallel_for algorytm do wywoływania skryptu równolegle.Ta funkcja wykorzystuje CCoInitializer klasy, aby zarządzać okresem istnienia biblioteki COM podczas każdej iteracji zadania.
// 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;
}
Aby użyć ParallelFibonacci funkcji do przykładu, Dodaj następujący kod przed wmain , funkcja zwraca.
// 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;
Wywoływanie skryptu z agenta
W poniższym przykładzie pokazano FibonacciScriptAgent klasy, która wywołuje procedurę skryptu do obliczenia nth fibonacciego.FibonacciScriptAgent Klasy używa wiadomość przechodzi do otrzymywania od głównego programu wprowadzania wartości do funkcji skryptów.run Metoda zarządza istnienia biblioteki COM w całym zadania.
// 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;
};
Poniższa funkcja AgentFibonacci, tworzy kilka FibonacciScriptAgent obiektów i używa wiadomość przechodzi do wysyłania kilku wprowadzania wartości do tych obiektów.
// 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;
}
Aby użyć AgentFibonacci funkcji do przykładu, Dodaj następujący kod przed wmain , funkcja zwraca.
// Use asynchronous agents to compute multiple
// Fibonacci numbers in parallel.
wcout << endl << L"Agent Fibonacci:" << endl;
if (FAILED(hr = AgentFibonacci(pScriptControl)))
return hr;
Kompletny przykład
Poniższy kod przedstawia przykład pełną, który wykorzystuje algorytmy równoległe i asynchronicznych agentów do wywołania procedury skryptu, który oblicza fibonacciego.
// 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;
}
W przykładzie daje następujące przykładowe dane wyjściowe.
Kompilowanie kodu
Skopiuj przykładowy kod i wklej go w projekcie programu Visual Studio lub wkleić go w pliku o nazwie równolegle scripts.cpp , a następnie uruchomić następujące polecenie w oknie wiersza polecenia programu Visual Studio.
cl.exe /EHsc parallel-scripts.cpp /link ole32.lib
Zobacz też
Koncepcje
Równoległość zadania (współbieżność środowiska wykonawczego)
Obsługa wyjątków we współbieżności środowiska wykonawczego
Harmonogram zadań (współbieżność środowiska wykonawczego)