Поскольку thread_interrupted
— исключение, то при вызове кода, который может быть прерван, следует принимать все обычные для исключений меры предосторожности, чтобы не было утечки ресурсов, и структуры данных оставались согласованными. Часто желательно завершать поток в случае прерывания, так чтобы исключение можно было просто передать вызывающей функции. Но если позволить исключению выйти за пределы функции потока, переданной конструктору std::thread
, то будет вызвана функция std::terminate()
, что приведёт к завершению всей программы. Чтобы не помещать обработчик catch(thread_interrupted)
в каждую функцию, которая передаётся interruptible_thread
, можно включить блок catch
в обертку, служащую для инициализации interrupt_flag
. Тогда распространять необработанное исключение будет безопасно, так как завершится лишь отдельный поток. Инициализация потока в конструкторе interruptible_thread
при таком подходе выглядит следующим образом:
internal_thread = std::thread([f, &p] {
p.set_value(&this_thread_interrupt_flag);
try {
f();
} catch(thread_interrupted const&) {}
});
А теперь рассмотрим конкретный пример, когда прерывание оказывается полезно.
9.2.7. Прерывание фоновых потоков при выходе из приложения
Представьте себе приложение для поиска в файловой системе настольного ПК. Оно должно не только взаимодействовать с пользователем, но и следить за состоянием файловой системы, обнаруживать изменения и обновлять свой индекс. Обычно такие операции поручаются фоновому потоку, чтобы пользовательский интерфейс мог реагировать на действия пользователя. Фоновый поток должен работать на протяжении всего времени жизни приложения; он запускается на этапе инициализации и трудится, пока приложение не завершится. Обычно это происходит при останове операционной системы, так как приложение должно постоянно поддерживать индекс в актуальном состоянии. Как бы то ни было, когда приложение завершается, надо аккуратно остановить и фоновые потоки, например, прервав их.
В следующем листинге показана возможная реализация управления потоками в такой программе.
Листинг 9.13. Фоновый мониторинг файловой системы
std::mutex config_mutex;
std::vector
void background_thread(int disk_id) {
while (true) {
interruption_point(); ←
(1)
fs_change fsc = get_fs_changes(disk_id); ←
(2)
if (fsc.has_changes()) {
update_index(fsc); ←
(3)
}
}
}
void start_background_processing() {
background_threads.push_back(
interruptible_thread(background_thread, disk_1));
background_threads.push_back(
interruptible_thread(background_thread, disk_2));
}
int main() {
start_background_processing(); ←
(4)
process_gui_until_exit(); ←
(5)
std::unique_lock
for (unsigned i = 0; i < background_threads.size(); ++i) {
background_threads[i].interrupt(); ←
(6)
}
for (unsigned i = 0; i < background_threads.size(); ++i) {
background_threads[i].join(); ←
(7)
}
}
В самом начале запускаются фоновые потоки (4). Затем главный поток продолжает обслуживать пользовательский интерфейс (5). Когда пользователь хочет выйти из приложения, фоновые потоки прерываются (6), после чего главный поток ждет их завершения (7), и только потом выходит сам. Каждый фоновый поток исполняет цикл, в котором следит за изменениями на диске (2) и обновляет индекс (3). На каждой итерации цикла поток проверяет, не прервали ли его, вызывая функцию interruption_point()
(1).