Obsługa wyjątków we współbieżności środowiska wykonawczego
Środowisko wykonawcze współbieżność używa obsługi do komunikacji wiele rodzajów błędów wyjątków C++.Błędy te obejmują nieprawidłowe użycie runtime, błędy czasu wykonania, takich jak awaria do nabywania zasobu i błędy, które występują w działają funkcje, które zapewniają do zadań i grup zadań.Kiedy zadania lub grupy zadań zgłasza wyjątek, środowiska wykonawczego posiada ten wyjątek i marszałków go do kontekstu, który czeka na zadania lub grupy zadań do końca.Dla składników takich jak lekka zadania i czynników środowiska wykonawczego nie zarządza wyjątki dla Ciebie.W takich przypadkach musi implementować własny mechanizm obsługi wyjątków.W tym temacie opisano sposób czasu wykonywania obsługi wyjątków, które są generowane przez zadania, grup zadań, zadań lekki i asynchronicznych agentów i sposobu odpowiedzi na wyjątki w aplikacji.
Kwestie kluczowe
Kiedy zadania lub grupy zadań zgłasza wyjątek, środowiska wykonawczego posiada ten wyjątek i marszałków go do kontekstu, który czeka na zadania lub grupy zadań do końca.
Jeśli to możliwe, należy surround każde wywołanie do concurrency::task::get i concurrency::task::wait z try/catch bloku do obsługi błędów, które można odzyskać od.Środowisko wykonawcze kończy aplikacji, jeśli zadanie zgłasza wyjątek a wyjątku nie jest wyłapywany przez zadanie, jeden z jego kontynuacji lub głównej aplikacji.
Zadaniowa kontynuacja zawsze działa; nie ma znaczenia, czy antecedent zadania została pomyślnie ukończona, wyjątek lub zostało anulowane.Kontynuacja oparte na wartościach nie jest uruchamiany, jeśli rzuca lub anuluje zadania antecedent.
Ponieważ zawsze uruchamiane kontynuacji opartych na zadaniach, należy rozważyć czy chcesz dodać na końcu swój łańcuch kontynuacji kontynuacji opartych na zadaniach.Pomoże to zagwarantować, że Twój kod rejestruje wszystkie wyjątki.
Środowisko wykonawcze wyrzuca concurrency::task_canceled pod numerem concurrency::task::get i to zadanie zostało anulowane.
Środowisko wykonawcze nie zarządza wyjątki dla zadań lekki i agentów.
W tym dokumencie
Zadania i kontynuacje
Grupy zadań i algorytmy równoległe
Wyjątki generowane przez środowisko wykonawcze
Wiele wyjątków
Anulowanie
Zadania lekkie
Agenci asynchroniczni
Zadania i kontynuacje
W tej sekcji opisano, jak środowisko wykonawcze obsługuje wyjątki, które są generowane przez concurrency::task obiektów i ich kontynuacji.Aby uzyskać więcej informacji na temat zadań i kontynuacji model, zobacz Równoległość zadania (współbieżność środowiska wykonawczego).
Kiedy należy zgłosić wyjątek w treści funkcji pracy, który jest przekazywany do task obiekt środowiska wykonawczego przechowuje ten wyjątek i marszałków go do kontekstu, który wywołuje concurrency::task::get lub concurrency::task::wait.Dokument Równoległość zadania (współbieżność środowiska wykonawczego) opisuje zadaniowa porównaniu kontynuacji oparte na wartościach, ale aby podsumować, kontynuacja opartej na wartościach przyjmuje parametr typu T i utrzymania opartych na zadaniach przyjmuje parametr typu task<T>.Jeśli zadanie, które generuje ma jeden lub więcej kontynuacji oparte na wartościach, kontynuacji tych nie są zaplanowane do uruchomienia.Poniższy przykład ilustruje to zachowanie:
// eh-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
wcout << L"Running a task..." << endl;
// Create a task that throws.
auto t = create_task([]
{
throw exception();
});
// Create a continuation that prints its input value.
auto continuation = t.then([]
{
// We do not expect this task to run because
// the antecedent task threw.
wcout << L"In continuation task..." << endl;
});
// Wait for the continuation to finish and handle any
// error that occurs.
try
{
wcout << L"Waiting for tasks to finish..." << endl;
continuation.wait();
// Alternatively, call get() to produce the same result.
//continuation.get();
}
catch (const exception& e)
{
wcout << L"Caught exception." << endl;
}
}
/* Output:
Running a task...
Waiting for tasks to finish...
Caught exception.
*/
Kontynuacja opartego na zadaniach umożliwia obsługę każdy wyjątek, który jest generowany przez zadanie antecedent.Zadaniowa kontynuacja zawsze działa; nie ma znaczenia, czy zadania została pomyślnie ukończona, wyjątek lub zostało anulowane.Gdy zadanie zgłasza wyjątek, jego kontynuacji opartego na zadaniach są zaplanowane do uruchomienia.Zadanie, które zawsze można znaleźć w poniższym przykładzie.Zadanie ma dwa kontynuacji; jeden jest oparte na wartościach, a druga jest oparte na zadaniach.Wyjątek oparty na zadanie zawsze działa, a zatem może przechwycić wyjątek, który jest generowany przez zadanie antecedent.Gdy przykładzie czeka na obu kontynuacji do zakończenia, wyjątku ponownie ponieważ wyjątek zadania zawsze jest generowany, kiedy task::get lub task::wait nazywa się.
// eh-continuations.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
wcout << L"Running a task..." << endl;
// Create a task that throws.
auto t = create_task([]() -> int
{
throw exception();
return 42;
});
//
// Attach two continuations to the task. The first continuation is
// value-based; the second is task-based.
// Value-based continuation.
auto c1 = t.then([](int n)
{
// We don't expect to get here because the antecedent
// task always throws.
wcout << L"Received " << n << L'.' << endl;
});
// Task-based continuation.
auto c2 = t.then([](task<int> previousTask)
{
// We do expect to get here because task-based continuations
// are scheduled even when the antecedent task throws.
try
{
wcout << L"Received " << previousTask.get() << L'.' << endl;
}
catch (const exception& e)
{
wcout << L"Caught exception from previous task." << endl;
}
});
// Wait for the continuations to finish.
try
{
wcout << L"Waiting for tasks to finish..." << endl;
(c1 && c2).wait();
}
catch (const exception& e)
{
wcout << L"Caught exception while waiting for all tasks to finish." << endl;
}
}
/* Output:
Running a task...
Waiting for tasks to finish...
Caught exception from previous task.
Caught exception while waiting for all tasks to finish.
*/
Zaleca się używanie opartego na zadaniach kontynuacji Aby przechwytywać wyjątków, które są w stanie obsłużyć.Ponieważ zawsze uruchamiane kontynuacji opartych na zadaniach, należy rozważyć czy chcesz dodać na końcu swój łańcuch kontynuacji kontynuacji opartych na zadaniach.Pomoże to zagwarantować, że Twój kod rejestruje wszystkie wyjątki.Poniższy przykład pokazuje łańcucha podstawowe kontynuacji oparte na wartościach.Wyrzuca trzecim zadaniem w łańcuchu, a zatem nie są uruchamiane kontynuacji wszelkich oparte na wartościach, które po nim następują.Utrzymanie końcowej jest jednak opartych na zadaniach i dlatego jest zawsze uruchamiany.To kontynuacja końcowy obsługuje wyjątek, który jest generowany przez trzecim zadaniem.
Zaleca się złapać najbardziej określone wyjątki, które można.Ten końcowy kontynuacji opartego na zadaniach można pominąć, jeśli nie ma określonych wyjątków do połowu.Każdy wyjątek pozostanie nieobsługiwany i może zakończyć aplikację.
// eh-task-chain.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
int n = 1;
create_task([n]
{
wcout << L"In first task. n = ";
wcout << n << endl;
return n * 2;
}).then([](int n)
{
wcout << L"In second task. n = ";
wcout << n << endl;
return n * 2;
}).then([](int n)
{
wcout << L"In third task. n = ";
wcout << n << endl;
// This task throws.
throw exception();
// Not reached.
return n * 2;
}).then([](int n)
{
// This continuation is not run because the previous task throws.
wcout << L"In fourth task. n = ";
wcout << n << endl;
return n * 2;
}).then([](task<int> previousTask)
{
// This continuation is run because it is value-based.
try
{
// The call to task::get rethrows the exception.
wcout << L"In final task. result = ";
wcout << previousTask.get() << endl;
}
catch (const exception&)
{
wcout << L"<exception>" << endl;
}
}).wait();
}
/* Output:
In first task. n = 1
In second task. n = 2
In third task. n = 4
In final task. result = <exception>
*/
Porada |
---|
Można użyć concurrency::task_completion_event::set_exception do kojarzenia wyjątek ze zdarzeniem ukończenia zadania.Dokument Równoległość zadania (współbieżność środowiska wykonawczego) w tym artykule opisano concurrency::task_completion_event klasy bardziej szczegółowo. |
CONCURRENCY::task_canceled jest typem wyjątku ważne runtime, który odnosi się do task.Środowisko wykonawcze wyrzuca task_canceled pod numerem task::get i to zadanie zostało anulowane. (Z drugiej strony, task::wait zwraca task_status::canceled i nie rzuca.) Można uchwycić i obsłużyć tego wyjątku z kontynuacją opartego na zadaniach lub podczas wywoływania task::get.Aby uzyskać więcej informacji o anulowaniu zadania, zobacz Anulowanie w PPL.
Przestroga |
---|
Nigdy nie rzucać task_canceled w kodzie.Wywołanie concurrency::cancel_current_task w zamian. |
Środowisko wykonawcze kończy aplikacji, jeśli zadanie zgłasza wyjątek a wyjątku nie jest wyłapywany przez zadanie, jeden z jego kontynuacji lub głównej aplikacji.Jeśli aplikacja zawiesza się, można skonfigurować Visual Studio do podziału, gdy C++ wyjątki są generowane.Po zdiagnozować lokalizacji nieobsługiwany wyjątek, użyj kontynuacja opartego na zadaniach go obsłużyć.
Sekcja Wyjątki generowane przez program obsługi w tym dokumencie opisano sposób pracy z wyjątkami runtime bardziej szczegółowo.
[U góry]
Grupy zadań i algorytmy równoległe
W tej sekcji opisano, jak środowisko wykonawcze obsługuje wyjątki, które są generowane przez grupy zadań.W tej sekcji dotyczą również algorytmy równoległe takich jak concurrency::parallel_for, ponieważ tych algorytmów tworzenia grup zadań.
Przestroga |
---|
Upewnij się, że wiesz, efekty, które mają wyjątki na zależnych od siebie zadań.Najważniejsze wskazówki dotyczące korzystania z zadań lub algorytmy równoległe obsługi wyjątków, zobacz temat zrozumieć jak anulowania i wyjątek obsługi wpływa na obiekt zniszczenie sekcję w najlepszych praktyk w temacie równoległych Biblioteka wzorców. |
Aby uzyskać więcej informacji dotyczących grup zadań, zobacz Równoległość zadania (współbieżność środowiska wykonawczego).Aby uzyskać więcej informacji dotyczących algorytmów równoległych, zobacz Algorytmy równoległe.
Kiedy należy zgłosić wyjątek w treści funkcji pracy, który jest przekazywany do concurrency::task_group lub concurrency::structured_task_group obiekt środowiska wykonawczego przechowuje ten wyjątek i marszałków go do kontekstu, który wywołuje concurrency::task_group::wait, concurrency::structured_task_group::wait, concurrency::task_group::run_and_wait, lub concurrency::structured_task_group::run_and_wait.Aparat plików wykonywalnych również zatrzymuje wszystkie aktywne zadania, które należą do grupy zadań (łącznie z datagramami grup zadań podrzędnych) i odrzuca wszystkie zadania, które nie zostały jeszcze rozpoczęte.
Poniższy przykład pokazuje podstawową strukturę funkcji pracy, która zgłasza wyjątek.W przykładzie użyto task_group mają być drukowane wartości dwóch point obiektów jednocześnie.print_point Funkcja pracy służy do drukowania wartości point obiektu do konsoli.Funkcja pracy zgłasza wyjątek, jeśli wartość wejściowa jest NULL.Aparat plików wykonywalnych przechowuje ten wyjątek i marszałków go do kontekstu, który wywołuje task_group::wait.
// eh-task-group.cpp
// compile with: /EHsc
#include <ppl.h>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
// Defines a basic point with X and Y coordinates.
struct point
{
int X;
int Y;
};
// Prints the provided point object to the console.
void print_point(point* pt)
{
// Throw an exception if the value is NULL.
if (pt == NULL)
{
throw exception("point is NULL.");
}
// Otherwise, print the values of the point.
wstringstream ss;
ss << L"X = " << pt->X << L", Y = " << pt->Y << endl;
wcout << ss.str();
}
int wmain()
{
// Create a few point objects.
point pt = {15, 30};
point* pt1 = &pt;
point* pt2 = NULL;
// Use a task group to print the values of the points.
task_group tasks;
tasks.run([&] {
print_point(pt1);
});
tasks.run([&] {
print_point(pt2);
});
// Wait for the tasks to finish. If any task throws an exception,
// the runtime marshals it to the call to wait.
try
{
tasks.wait();
}
catch (const exception& e)
{
wcerr << L"Caught exception: " << e.what() << endl;
}
}
Ten przykład generuje następujące wyniki.
Pełny przykład używa obsługi wyjątków w grupę zadań, zobacz Porady: Użyj obsługi wyjątków, aby przerwać pętlę równoległą.
[U góry]
Wyjątki generowane przez środowisko wykonawcze
Wyjątek mogą być wynikiem rozmów w czasie wykonywania.Większość typów wyjątków, z wyjątkiem concurrency::task_canceled i concurrency::operation_timed_out, wskazywać na błąd programistyczny.Te błędy są zwykle nie do odzyskania i dlatego nie należy złowionych ani obsługiwane przez kod aplikacji.Zalecamy tylko złapać lub obsługi nieodwracalne błędy w kodzie aplikacji, gdy konieczne jest zdiagnozowanie błędy programowania.Jednakże opis typów wyjątków, które są zdefiniowane w czasie wykonywania może pomóc w zdiagnozowaniu błędy programowania.
Mechanizm obsługi wyjątków jest taka sama dla wyjątków, które są generowane przez program obsługi wyjątków, które są generowane przez funkcje pracy.Na przykład concurrency::receive funkcja wyrzuca operation_timed_out kiedy nie otrzyma wiadomości w określonym okresie.Jeśli receive zgłasza wyjątek w funkcji pracy, przechodzą do grupy zadań, środowiska wykonawczego przechowuje ten wyjątek i marszałków go do kontekstu, który wywołuje task_group::wait, structured_task_group::wait, task_group::run_and_wait, lub structured_task_group::run_and_wait.
W poniższym przykładzie użyto concurrency::parallel_invoke algorytm na równoległe uruchamianie dwóch zadań.Pierwsze zadanie czeka pięć sekund, a następnie wysyła wiadomość do bufor komunikatów.Drugie zadanie używa receive funkcja oczekiwania trzy sekundy do odbierania wiadomości od tego samego buforu wiadomość.receive Funkcja wyrzuca operation_timed_out Jeżeli nie otrzyma wiadomości w danym okresie.
// eh-time-out.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
single_assignment<int> buffer;
int result;
try
{
// Run two tasks in parallel.
parallel_invoke(
// This task waits 5 seconds and then sends a message to
// the message buffer.
[&] {
wait(5000);
send(buffer, 42);
},
// This task waits 3 seconds to receive a message.
// The receive function throws operation_timed_out if it does
// not receive a message in the specified time period.
[&] {
result = receive(buffer, 3000);
}
);
// Print the result.
wcout << L"The result is " << result << endl;
}
catch (operation_timed_out&)
{
wcout << L"The operation timed out." << endl;
}
}
Ten przykład generuje następujące wyniki.
Zapobiegające Nienormalne zakończenie aplikacji, upewnij się, że Twój kod wywołuje w czasie wykonywania, obsługuje wyjątki.Również obsługiwać wyjątków podczas wywoływania na zewnętrzny kod, który używa Runtime współbieżności na przykład innej biblioteki.
[U góry]
Wiele wyjątków
Jeżeli zadanie lub równoległy algorytm otrzymuje wiele wyjątków, środowiska wykonawczego marszałków tylko jeden z tych wyjątków kontekście wywołującego.Środowisko wykonawcze nie gwarantuje wyjątków, które to marszałków.
W poniższym przykładzie użyto parallel_for algorytm numery do konsoli.Jeśli wartość wejściowa jest mniejsza niż pewną wartość minimalna lub większa niż pewną wartość maksymalną, zgłasza wyjątek.W tym przykładzie wielu funkcji pracy można zgłosić wyjątek.
// eh-multiple.cpp
// compile with: /EHsc
#include <ppl.h>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
int wmain()
{
const int min = 0;
const int max = 10;
// Print values in a parallel_for loop. Use a try-catch block to
// handle any exceptions that occur in the loop.
try
{
parallel_for(-5, 20, [min,max](int i)
{
// Throw an exeception if the input value is less than the
// minimum or greater than the maximum.
// Otherwise, print the value to the console.
if (i < min)
{
stringstream ss;
ss << i << ": the value is less than the minimum.";
throw exception(ss.str().c_str());
}
else if (i > max)
{
stringstream ss;
ss << i << ": the value is greater than than the maximum.";
throw exception(ss.str().c_str());
}
else
{
wstringstream ss;
ss << i << endl;
wcout << ss.str();
}
});
}
catch (exception& e)
{
// Print the error to the console.
wcerr << L"Caught exception: " << e.what() << endl;
}
}
Poniżej przedstawiono przykładowe dane wyjściowe tego przykładu.
[U góry]
Anulowanie
Nie wszystkie wyjątki, jeżeli wystąpił błąd.Algorytm wyszukiwania może na przykład użyć obsługi wyjątków zatrzymać jego skojarzonego zadania po znalezieniu wynik.Aby uzyskać więcej informacji dotyczących sposobu korzystania z mechanizmów anulowania w kodzie, zobacz Anulowanie w PPL.
[U góry]
Zadania lekkie
Lekki zadanie jest zadaniem zaplanowanym bezpośrednio z concurrency::Scheduler obiektu.Lekki zadania wykonać mniejszego nakładu pracy niż zwyczajne zadania.Jednak środowiska wykonawczego nie przechwytywać wyjątków, które są generowane przez lekki zadania.Zamiast tego przechwycono przez program obsługi nieobsługiwany wyjątek, który domyślnie kończy proces.W związku z tym należy użyć odpowiedniego mechanizmu obsługi błędów w aplikacji.Aby uzyskać więcej informacji na temat zadań lekki, zobacz Harmonogram zadań (współbieżność środowiska wykonawczego).
[U góry]
Agenci asynchroniczni
Podobnie jak zadania lekki środowiska wykonawczego nie zarządza wyjątków, które są generowane przez agentów asynchronicznego.
Poniższy przykład przedstawia sposób obsługi wyjątków w klasie, która wynika z concurrency::agent.W tym przykładzie definiuje points_agent klasy.points_agent::run Odczyty metoda point z bufor komunikatów obiekty i drukuje je do konsoli.run Metoda zgłasza wyjątek, gdy otrzymuje ona NULL wskaźnik.
run Metoda otacza wszystkie prace w try-catch bloku.catch Blok przechowuje wyjątek w buforze wiadomość.Aplikacja sprawdza, czy agent napotkał błąd poprzez czytanie z tego buforu, po zakończeniu agenta.
// eh-agents.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Defines a point with x and y coordinates.
struct point
{
int X;
int Y;
};
// Informs the agent to end processing.
point sentinel = {0,0};
// An agent that prints point objects to the console.
class point_agent : public agent
{
public:
explicit point_agent(unbounded_buffer<point*>& points)
: _points(points)
{
}
// Retrieves any exception that occurred in the agent.
bool get_error(exception& e)
{
return try_receive(_error, e);
}
protected:
// Performs the work of the agent.
void run()
{
// Perform processing in a try block.
try
{
// Read from the buffer until we reach the sentinel value.
while (true)
{
// Read a value from the message buffer.
point* r = receive(_points);
// In this example, it is an error to receive a
// NULL point pointer. In this case, throw an exception.
if (r == NULL)
{
throw exception("point must not be NULL");
}
// Break from the loop if we receive the
// sentinel value.
else if (r == &sentinel)
{
break;
}
// Otherwise, do something with the point.
else
{
// Print the point to the console.
wcout << L"X: " << r->X << L" Y: " << r->Y << endl;
}
}
}
// Store the error in the message buffer.
catch (exception& e)
{
send(_error, e);
}
// Set the agent status to done.
done();
}
private:
// A message buffer that receives point objects.
unbounded_buffer<point*>& _points;
// A message buffer that stores error information.
single_assignment<exception> _error;
};
int wmain()
{
// Create a message buffer so that we can communicate with
// the agent.
unbounded_buffer<point*> buffer;
// Create and start a point_agent object.
point_agent a(buffer);
a.start();
// Send several points to the agent.
point r1 = {10, 20};
point r2 = {20, 30};
point r3 = {30, 40};
send(buffer, &r1);
send(buffer, &r2);
// To illustrate exception handling, send the NULL pointer to the agent.
send(buffer, reinterpret_cast<point*>(NULL));
send(buffer, &r3);
send(buffer, &sentinel);
// Wait for the agent to finish.
agent::wait(&a);
// Check whether the agent encountered an error.
exception e;
if (a.get_error(e))
{
cout << "error occurred in agent: " << e.what() << endl;
}
// Print out agent status.
wcout << L"the status of the agent is: ";
switch (a.status())
{
case agent_created:
wcout << L"created";
break;
case agent_runnable:
wcout << L"runnable";
break;
case agent_started:
wcout << L"started";
break;
case agent_done:
wcout << L"done";
break;
case agent_canceled:
wcout << L"canceled";
break;
default:
wcout << L"unknown";
break;
}
wcout << endl;
}
Ten przykład generuje następujące wyniki.
Ponieważ try-catch bloku istnieje poza while pętli, agent kończą przetwarzanie po napotkaniu pierwszego błędu.Jeśli try-catch był blok wewnątrz while pętli, agent będzie nadal po wystąpieniu błędu.
W tym przykładzie przechowuje wyjątki w buforze wiadomości, dzięki czemu innego składnika można monitorować agenta błędy podczas jego działania.W poniższym przykładzie użyto concurrency::single_assignment obiektu do przechowywania błędu.W przypadku gdy agent obsługuje wiele wyjątków single_assignment klasa przechowuje tylko pierwszą wiadomość przekazywana do niego.Aby przechowywać tylko ostatni wyjątek, użyj concurrency::overwrite_buffer klasy.Aby przechowywać wszystkie wyjątki, należy użyć concurrency::unbounded_buffer klasy.Aby uzyskać więcej informacji dotyczących tych bloków komunikatów, zobacz Bloki komunikatów asynchronicznych.
Aby uzyskać więcej informacji na temat agentów asynchronicznych, zobacz Agenci asynchroniczni.
[U góry]
Podsumowanie
[U góry]
Zobacz też
Koncepcje
Współbieżność środowiska wykonawczego
Równoległość zadania (współbieżność środowiska wykonawczego)