[Программирование, C++] Новый поток в C++20: std::jthread (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет, Хабр! Перевод статьи подготовлен в рамках курса "C++ Developer. Professional"
Один из участников моего семинара в рамках CppCon 2018 спросил меня: «Может ли std::thread быть прерван (interrupted)?». Мой ответ тогда был — нет, но это уже не совсем так. С C++20 мы можем получить std::jthread (в итоге все таки получили — прим. переводчика).Позвольте мне развить тему, поднятую на CppCon 2018. Во время перерыва в моем семинаре, посвященному параллелизму, я побеседовал с Николаем (Йосуттисом). Он спросил меня, что я думаю о новом предложении P0660: Cooperatively Interruptible Joining Thread. На тот момент я ничего не знал об этом предложении. Следует отметить, что Николай является одним из авторов этого предложения (наряду с Хербом Саттером и Энтони Уильямсом). Сегодняшняя статья посвящена будущему параллелизма в C++. Ниже я привел общую картину параллелизма в текущем и грядущем C++.
Из названия документа Cooperatively Interruptible Joining Thread (совместно прерываемый присоединяемый поток) вы можете догадаться, что новый поток имеет две новые возможности: прерываемость (interruptible) и автоматическое присоединение (automatically joining, здесь и далее «присоединение» — блокировка вызывающего потока до завершения выполнения, результат вызова метода join() — прим. переводчика). Позвольте мне сначала рассказать вам об автоматическом присоединении.Автоматическое присоединениеЭто неинтуитивное поведение std::thread. Если std::thread все еще является joinable, то в его деструкторе вызывается std::terminate. Поток thr является joinable, если ни thr.join(), ни thr.detach() еще не были вызваны.
// threadJoinable.cpp
#include <iostream>
#include <thread>
int main(){
std::cout << std::endl;
std::cout << std::boolalpha;
std::thread thr{[]{ std::cout << "Joinable std::thread" << std::endl; }};
std::cout << "thr.joinable(): " << thr.joinable() << std::endl;
std::cout << std::endl;
}
При выполнении программа терминируется.
Оба потока терминируются. На втором запуске поток th имеет достаточно времени, чтобы отобразить свое сообщение: «Joinable std::thread».В следующем примере я заменяю хедер <thread> на "jthread.hpp" и использую std::jthread из грядущего стандарта C++.
// jthreadJoinable.cpp
#include <iostream>
#include "jthread.hpp"
int main(){
std::cout << std::endl;
std::cout << std::boolalpha;
std::jthread thr{[]{ std::cout << "Joinable std::thread" << std::endl; }};
std::cout << "thr.joinable(): " << thr.joinable() << std::endl;
std::cout << std::endl;
}
Теперь поток thr автоматически присоединяется в своем деструкторе, если он все еще является joinable, как, например, в этом примере.
Прерывание std::jthreadЧтобы было от чего отталкиваться, позвольте мне продемонстрировать вам простой пример.
// interruptJthread.cpp
#include "jthread.hpp"
#include <chrono>
#include <iostream>
using namespace::std::literals;
int main(){
std::cout << std::endl;
std::jthread nonInterruptable([]{ // (1)
int counter{0};
while (counter < 10){
std::this_thread::sleep_for(0.2s);
std::cerr << "nonInterruptable: " << counter << std::endl;
++counter;
}
});
std::jthread interruptable([](std::interrupt_token itoken){ // (2)
int counter{0};
while (counter < 10){
std::this_thread::sleep_for(0.2s);
if (itoken.is_interrupted()) return; // (3)
std::cerr << "interruptable: " << counter << std::endl;
++counter;
}
});
std::this_thread::sleep_for(1s);
std::cerr << std::endl;
std::cerr << "Main thread interrupts both jthreads" << std:: endl;
nonInterruptable.interrupt();
interruptable.interrupt(); // (4)
std::cout << std::endl;
}
Я запустил в main два потока, nonInterruptable, который нельзя прерывать, и interruptable, который можно (строки 1 и 2). В отличие от потока nonInterruptable, поток interruptable, получает std::interrupt_token и использует его в строке 3, чтобы проверить, был ли он прерван: itoken.is_interrupted(). В случае прерывания в лямбде срабатывает return и, следовательно, поток завершается. Вызов interruptable.interrupt() (строка 4) триггерит завершение потока. Аналогичный вызов nonInterruptable.interrupt() не сработает для потока nonInterruptable, который, как мы видим, продолжает свое выполнение.
Вот более подробная информация о токенах прерывания (interrupt tokens), присоединяющихся потоках и условных переменных.Токены прерыванияТокен прерывания std::interrupt_token моделирует совместное владение (shared ownership) и может использоваться для сигнализирования о прерывании, если токен валиден. Он предоставляет три метода: valid, is_interrupted, и interrupt.
itoken.valid() — true, если токен прерывания может быть использован для сигнализировании о прерыванииitoken.is_interrupted() — true, если был инициализирован с true или был вызван метода interrupt()itoken.interrupt() — если !valid() или is_interrupted(), то вызов метода не возымеет эффекта. В противном случае, сигнализирует о прерывании посредством itoken.is_interrupted() == true. Возвращает значение is_interrupted()Если токен прерывания должен быть временно отключен, вы можете заменить его дефолтным токеном. Дефолтный токен не валиден. Следующий фрагмент кода демонстрирует, как включать и отключать возможность потока принимать сигналы.
std::jthread jthr([](std::interrupt_token itoken){
...
std::interrupt_token interruptDisabled;
std::swap(itoken, interruptDisabled); // (1)
...
std::swap(itoken, interruptDisabled); // (2)
...
}
std::interrupt_token interruptDisabled не валиден. Это означает, что поток не может принять прерывание между строками (1) и (2), но после строки (2) уже может.Присоединение потоковstd::jhread представляет собой std::thread с дополнительным функционалом, реализующим сигнализирование о прерывании и автоматическое присоединение. Для поддержки этой функциональности у него есть std::interrupt_token.Новые перегрузки Wait для условных переменныхДве вариации wait wait_for и wait_until из std::condition_variable получат новые перегрузки. Они принимают std::interrupt_token.
template <class Predicate>
bool wait_until(unique_lock<mutex>& lock,
Predicate pred,
interrupt_token itoken);
template <class Rep, class Period, class Predicate>
bool wait_for(unique_lock<mutex>& lock,
const chrono::duration<Rep, Period>& rel_time,
Predicate pred,
interrupt_token itoken);
template <class Clock, class Duration, class Predicate>
bool wait_until(unique_lock<mutex>& lock,
const chrono::time_point<Clock, Duration>& abs_time,
Predicate pred,
interrupt_token itoken);
Новые перегрузки требует предикат. Эти версии гарантированно получают уведомления, если поступает сигнал о прерывании для переданного им std::interrupt_token itoken. После вызовов wait вы можете проверить, не произошло ли прерывание.
cv.wait_until(lock, predicate, itoken);
if (itoken.is_interrupted()){
// interrupt occurred
}
Что дальше?Как я и обещал в своей последней статье, следующая статья будет посвящена оставшимся правилам определения концептов (concepts).
Узнать подробнее о курсе "C++ Developer. Professional".
Смотреть запись демо-занятия по теме «Области видимости и невидимости»: участники вместе с экспертом попробовали реализовать класс общего назначения и запустить несколько unit-тестов с использованием googletest.
===========
Источник:
habr.com
===========
===========
Автор оригинала: modernescpp.com
===========Похожие новости:
- [Разработка веб-сайтов, JavaScript, Программирование, HTML] Красивое радио для браузера
- [Разработка веб-сайтов, JavaScript, Программирование, ReactJS] React Social App
- [Open source, Программирование, Геоинформационные сервисы, Визуализация данных, Научно-популярное] Google Earth Engine (GEE) как общедоступный суперкомпьютер
- [Разработка веб-сайтов, Программирование, Компиляторы, IT-стандарты] Wasp — DSL для разработки веб-приложений
- [C++, Алгоритмы, Параллельное программирование] Часть 1. MPI — Введение и первая программа
- [Программирование микроконтроллеров, Электроника для начинающих] На распутье — Ардуино, Cи или Ассемблер?
- [Программирование, Dart] Ускоряем Dart. Нативно, недорого
- [Программирование, C++, Программирование микроконтроллеров, Производство и разработка электроники] Создание аналога посмертного сore dump для микроконтроллера
- [Программирование, Алгоритмы] Детская сказка программисту на ночь
- [Big Data, Машинное обучение] Как управлять проектами машинного обучения и data science (перевод)
Теги для поиска: #_programmirovanie (Программирование), #_c++, #_cplusplus, #_oblasti_vidimosti (области видимости), #_programming, #_preryvanija (прерывания), #_parallelizm (параллелизм), #_blog_kompanii_otus (
Блог компании OTUS
), #_programmirovanie (
Программирование
), #_c++
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:02
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет, Хабр! Перевод статьи подготовлен в рамках курса "C++ Developer. Professional"
Один из участников моего семинара в рамках CppCon 2018 спросил меня: «Может ли std::thread быть прерван (interrupted)?». Мой ответ тогда был — нет, но это уже не совсем так. С C++20 мы можем получить std::jthread (в итоге все таки получили — прим. переводчика).Позвольте мне развить тему, поднятую на CppCon 2018. Во время перерыва в моем семинаре, посвященному параллелизму, я побеседовал с Николаем (Йосуттисом). Он спросил меня, что я думаю о новом предложении P0660: Cooperatively Interruptible Joining Thread. На тот момент я ничего не знал об этом предложении. Следует отметить, что Николай является одним из авторов этого предложения (наряду с Хербом Саттером и Энтони Уильямсом). Сегодняшняя статья посвящена будущему параллелизма в C++. Ниже я привел общую картину параллелизма в текущем и грядущем C++. Из названия документа Cooperatively Interruptible Joining Thread (совместно прерываемый присоединяемый поток) вы можете догадаться, что новый поток имеет две новые возможности: прерываемость (interruptible) и автоматическое присоединение (automatically joining, здесь и далее «присоединение» — блокировка вызывающего потока до завершения выполнения, результат вызова метода join() — прим. переводчика). Позвольте мне сначала рассказать вам об автоматическом присоединении.Автоматическое присоединениеЭто неинтуитивное поведение std::thread. Если std::thread все еще является joinable, то в его деструкторе вызывается std::terminate. Поток thr является joinable, если ни thr.join(), ни thr.detach() еще не были вызваны. // threadJoinable.cpp
#include <iostream> #include <thread> int main(){ std::cout << std::endl; std::cout << std::boolalpha; std::thread thr{[]{ std::cout << "Joinable std::thread" << std::endl; }}; std::cout << "thr.joinable(): " << thr.joinable() << std::endl; std::cout << std::endl; } Оба потока терминируются. На втором запуске поток th имеет достаточно времени, чтобы отобразить свое сообщение: «Joinable std::thread».В следующем примере я заменяю хедер <thread> на "jthread.hpp" и использую std::jthread из грядущего стандарта C++. // jthreadJoinable.cpp
#include <iostream> #include "jthread.hpp" int main(){ std::cout << std::endl; std::cout << std::boolalpha; std::jthread thr{[]{ std::cout << "Joinable std::thread" << std::endl; }}; std::cout << "thr.joinable(): " << thr.joinable() << std::endl; std::cout << std::endl; } Прерывание std::jthreadЧтобы было от чего отталкиваться, позвольте мне продемонстрировать вам простой пример. // interruptJthread.cpp
#include "jthread.hpp" #include <chrono> #include <iostream> using namespace::std::literals; int main(){ std::cout << std::endl; std::jthread nonInterruptable([]{ // (1) int counter{0}; while (counter < 10){ std::this_thread::sleep_for(0.2s); std::cerr << "nonInterruptable: " << counter << std::endl; ++counter; } }); std::jthread interruptable([](std::interrupt_token itoken){ // (2) int counter{0}; while (counter < 10){ std::this_thread::sleep_for(0.2s); if (itoken.is_interrupted()) return; // (3) std::cerr << "interruptable: " << counter << std::endl; ++counter; } }); std::this_thread::sleep_for(1s); std::cerr << std::endl; std::cerr << "Main thread interrupts both jthreads" << std:: endl; nonInterruptable.interrupt(); interruptable.interrupt(); // (4) std::cout << std::endl; } Вот более подробная информация о токенах прерывания (interrupt tokens), присоединяющихся потоках и условных переменных.Токены прерыванияТокен прерывания std::interrupt_token моделирует совместное владение (shared ownership) и может использоваться для сигнализирования о прерывании, если токен валиден. Он предоставляет три метода: valid, is_interrupted, и interrupt. itoken.valid() — true, если токен прерывания может быть использован для сигнализировании о прерыванииitoken.is_interrupted() — true, если был инициализирован с true или был вызван метода interrupt()itoken.interrupt() — если !valid() или is_interrupted(), то вызов метода не возымеет эффекта. В противном случае, сигнализирует о прерывании посредством itoken.is_interrupted() == true. Возвращает значение is_interrupted()Если токен прерывания должен быть временно отключен, вы можете заменить его дефолтным токеном. Дефолтный токен не валиден. Следующий фрагмент кода демонстрирует, как включать и отключать возможность потока принимать сигналы. std::jthread jthr([](std::interrupt_token itoken){
... std::interrupt_token interruptDisabled; std::swap(itoken, interruptDisabled); // (1) ... std::swap(itoken, interruptDisabled); // (2) ... } template <class Predicate>
bool wait_until(unique_lock<mutex>& lock, Predicate pred, interrupt_token itoken); template <class Rep, class Period, class Predicate> bool wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred, interrupt_token itoken); template <class Clock, class Duration, class Predicate> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred, interrupt_token itoken); cv.wait_until(lock, predicate, itoken);
if (itoken.is_interrupted()){ // interrupt occurred } Узнать подробнее о курсе "C++ Developer. Professional".
Смотреть запись демо-занятия по теме «Области видимости и невидимости»: участники вместе с экспертом попробовали реализовать класс общего назначения и запустить несколько unit-тестов с использованием googletest. =========== Источник: habr.com =========== =========== Автор оригинала: modernescpp.com ===========Похожие новости:
Блог компании OTUS ), #_programmirovanie ( Программирование ), #_c++ |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:02
Часовой пояс: UTC + 5