#c #exception #synchronization #cloning
#c #исключение #синхронизация #клонирование
Вопрос:
Я использую библиотеку boost для обработки потоков и синхронизации в своем приложении.
Прежде всего, я должен сказать, что исключения внутри потоков при синхронизации — это для меня что-то компилируемое. В любом случае ниже приведен псевдокод, которого я хочу достичь. Я хочу, чтобы синхронизированные потоки выдавали то же исключение, которое, ВОЗМОЖНО, было выдано из потока, выполняющего notify . Как я могу этого добиться?
Не удалось найти какие-либо темы из Stack Overflow, касающиеся генерирования исключений при взаимодействии между потоками с использованием модели boost threading
Заранее большое спасибо!
// mutex and scondition variable for the problem
mutable boost::mutex conditionMutex;
mutable boost::condition_variable condition;
inline void doTheThing() const {
if (noone doing the thing) {
try {
doIt()
// I succeeded
failed = false;
condition.notify_all();
}
catch (...) {
// I failed to do it
failed = true;
condition.notify_all();
throw
}
else {
boost::mutex::scoped_lock lock(conditionMutex);
condition.wait(lock);
if (failed) {
// throw the same exception that was thrown from
// thread doing notify_all
}
}
}
Ответ №1:
Итак, вы хотите, чтобы первый поток, который попадет в цель, doTheThing()
вызывал doIt()
, а все последующие потоки, которые попадут в цель, doTheThing()
ждали завершения вызова первого потока doIt()
, прежде чем продолжить.
Я думаю, это должно сработать:
boost::mutex conditionMutex; // mutable qualifier not needed
bool failed = false;
bool done = false;
inline void doTheThing() const {
boost::unique_lock uql(conditionMutex);
if (!done) {
done = true;
try {
doIt();
failed = false;
}
catch (...) {
failed = true;
throw
}
}
else if (failed)
{
uql.unlock();
// now this thread knows that another thread called doIt() and an exception
// was thrown in that thread.
}
}
Важные примечания:
Каждый вызывающий поток doTheThing()
должен получать блокировку. Обойти это невозможно. Вы синхронизируете потоки, и для того, чтобы поток знал что-либо о том, что происходит в другом потоке, он должен получить блокировку. (Или он может использовать операции с атомной памятью, но это более продвинутый метод.) Переменные failed
и done
защищены conditionMutex
.
C вызовет деструктор uql
при обычном завершении функции или при выбрасывании исключения.
РЕДАКТИРОВАТЬ О, и что касается передачи исключения всем другим потокам, забудьте об этом, это почти невозможно, и это не то, как все делается в C . Вместо этого каждый поток может проверить, успешно ли вызван первый поток doIt()
в указанном выше месте.
РЕДАКТИРОВАТЬ Нет языковой поддержки для распространения исключения в другой поток. Проблему распространения исключений в другой поток можно обобщить на передачу сообщений в другой поток. Существует множество библиотечных решений проблемы передачи сообщений между потоками ( boost::asio::io_service::post() ), и вы могли бы передать сообщение, содержащее исключение, с инструкциями по созданию этого исключения при получении сообщения. Однако это плохая идея. Исключения генерируются только при возникновении ошибки, которая не позволяет вам развернуть стек вызовов с помощью обычного возврата функции. Вот что такое исключение — альтернативный способ возврата из функции, когда возврат обычным способом не имеет смысла.
Комментарии:
1. Спасибо за ответ. Я знаю, что мне нужно заблокировать оператор if, чтобы пропустить другие. Я просто исключил блокировку из псевдокода. В вашем решении требуется, чтобы каждый поток проходил через doIt(). Я хочу, чтобы заблокированные потоки ждали, пока рабочий поток завершит то, что он делает. Реальная проблема заключалась в том, как распространить возможное исключение на синхронизированные потоки. Так вы хотите сказать, что это вообще невозможно?
2. @Lauri 1 из 2: Посмотрите внимательно, проходит только первый поток
doIt()
, остальные потоки будут пропущеныdoIt()
после проверкиdone
.3. @Lauri 2 из 2: Я уточнил свой ответ, см. Выше.
4. Если вы используете текущую версию C (C 11), существует языковая поддержка для распространения исключений на другие потоки. Используйте std::current_exception(), чтобы получить объект std::exception_ptr, перенести его в другой поток, а затем вызвать для него std::rethrow_exception(). Или вы можете использовать std::promise /std::future для этого.