SlideShare une entreprise Scribd logo
1  sur  52
Télécharger pour lire hors ligne
RAII потоки и cancellation_token в C++
Борис Сазонов, 2016
Disclaimer
В этой презентации вы встретите:
✓ Извращения в форматировании кода
✓ C-style комментарии
✓ Чересчур лаконичные имена классов
✓ Неэффективный код без использования move и forward
✓ Отсутствие проверки ошибок
✓ Невидимый “using namespace std”
✓ Прочие гадости
Всё это сделано для того, чтобы сохранить разумный размер шрифта. Всегда пожалуйста.
Problem, officer?
class worker {
atomic<bool> _alive;
thread _thread;
public:
worker() : _alive(true) {
_thread = thread(bind(&worker::work, this));
// Something else...
}
~worker() {
_alive = false;
_thread.join();
}
private:
void work() {
while (_alive)
do_work();
}
};
Problem, officer?
class worker {
atomic<bool> _alive;
thread _thread;
public:
worker() : _alive(true) {
_thread = thread(bind(&worker::work, this));
throw runtime_error("Something can throw!");
}
~worker() {
_alive = false;
_thread.join();
}
private:
void work() {
while (_alive)
do_work();
}
};
Bang!
class worker {
atomic<bool> _alive;
thread _thread;
public:
worker() : _alive(true) {
_thread = thread(bind(&worker::work, this));
throw runtime_error("Something can throw!"); // Bang! Calls terminate()
}
~worker() {
_alive = false;
_thread.join();
}
private:
void work() {
while (_alive)
do_work();
}
};
Зловещий деструктор
30.3.1.3 thread destructor
~thread();
If joinable() then terminate(), otherwise no effects.
Зловещий деструктор
30.3.1.3 thread destructor
~thread();
If joinable() then terminate(), otherwise no effects.
Попробуем что-нибудь с этим сделать:
class raii_thread {
thread _impl;
public:
// raii_thread constructors and methods
~raii_thread() {
if (_impl.joinable())
reset();
}
void reset()
{ /* ??? */ }
};
void reset()
{ _impl.detach(); }
Спасение от зловещего деструктора: detach vs. join
Спасение от зловещего деструктора: detach vs. join
void reset()
{ _impl.detach(); }
class worker {
atomic<bool> _alive;
raii_thread _thread;
public:
worker() : _alive(true) {
_thread = raii_thread(bind(&worker::work, this));
// Something else, possibly throw
}
~worker() {
_alive = false;
_thread.reset();
}
private:
void work() {
while (_alive)
do_work();
}
};
Спасение от зловещего деструктора: detach vs. join
void reset()
{ _impl.detach(); }
− Небезопасно - в любой непонятной
ситуации поток будет работать с
мёртвым объектом
− Нарушение RAII
− Личная неприязнь
class worker {
atomic<bool> _alive;
raii_thread _thread;
public:
worker() : _alive(true) {
_thread = raii_thread(bind(&worker::work, this));
// Something else, possibly throw
}
~worker() {
_alive = false;
_thread.reset();
}
private:
void work() {
while (_alive)
do_work();
}
};
Спасение от зловещего деструктора: detach vs. join
void reset()
{ _impl.detach(); }
− Небезопасно - в любой непонятной
ситуации поток будет работать с
мёртвым объектом
− Нарушение RAII
− Личная неприязнь
void reset()
{ _impl.join(); }
class worker {
atomic<bool> _alive;
raii_thread _thread;
public:
worker() : _alive(true) {
_thread = raii_thread(bind(&worker::work, this));
// Something else, possibly throw
}
~worker() {
_alive = false;
_thread.reset();
}
private:
void work() {
while (_alive)
do_work();
}
};
Спасение от зловещего деструктора: detach vs. join
void reset()
{ _impl.detach(); }
− Небезопасно - в любой непонятной
ситуации поток будет работать с
мёртвым объектом
− Нарушение RAII
− Личная неприязнь
void reset()
{ _impl.join(); }
+ RAII über alles
− Отсутствие у потока возможности
завершить исполняемую функцию, что
приводит к зависанию в деструкторе
class worker {
atomic<bool> _alive;
raii_thread _thread;
public:
worker() : _alive(true) {
_thread = raii_thread(bind(&worker::work, this));
// Something else, possibly throw
}
~worker() {
_alive = false;
_thread.reset();
}
private:
void work() {
while (_alive)
do_work();
}
};
Interrupt (pthread_cancel, boost::thread::interrupt и т.д.)
Потоку можно отправить запрос на прерывание исполнения. Тогда в целевом потоке из системных
вызовов (read, write, и т.д.) вылетит исключение специального типа. Ещё есть специальная функция,
который позволяет проверить, не был ли прерван текущий поток (pthread_testcancel, boost::thread::
interruption_point, и т.д.).
Способы прервать выполнение функции: Interrupt
Interrupt (pthread_cancel, boost::thread::interrupt и т.д.)
Потоку можно отправить запрос на прерывание исполнения. Тогда в целевом потоке из системных
вызовов (read, write, и т.д.) вылетит исключение специального типа. Ещё есть специальная функция,
который позволяет проверить, не был ли прерван текущий поток (pthread_testcancel, boost::thread::
interruption_point, и т.д.).
+ Прерывает ожидание на условных переменных
+ Прерывает блокирующие функции ОС (read, write, send, recv, и т.д.)
+ Практически невозможно игнорировать
Способы прервать выполнение функции: Interrupt
Interrupt (pthread_cancel, boost::thread::interrupt и т.д.)
Потоку можно отправить запрос на прерывание исполнения. Тогда в целевом потоке из системных
вызовов (read, write, и т.д.) вылетит исключение специального типа. Ещё есть специальная функция,
который позволяет проверить, не был ли прерван текущий поток (pthread_testcancel, boost::thread::
interruption_point, и т.д.).
+ Прерывает ожидание на условных переменных
+ Прерывает блокирующие функции ОС (read, write, send, recv, и т.д.)
+ Практически невозможно игнорировать
− Исключение из системных вызовов станет сюрпризом для многих библиотек, написанных на C.
Вероятный результат - утечка ресурсов, не разблокированные мьютексы и т.д.
− Cистемные вызовы в деструкторах могут кинуть исключение
− Сложности с портированием - на многих ОС pthread_cancel или аналогов нет (и не будет)
− C++ STL нет interrupt или аналога
− В C++14 condition_variable::wait не кидает исключений
− Необратимость - нельзя переиспользовать поток
Способы прервать выполнение функции: Interrupt
Способы прервать выполнение функции: булев флаг
Булев флаг в нашем примере с worker’ом - это atomic<bool> _alive
В конструкторе:
_alive = true;
В деструкторе:
_alive = false;
В прерываемой функции:
void work() {
while (_alive)
do_work();
}
Способы прервать выполнение функции: булев флаг
Булев флаг в нашем примере с worker’ом - это atomic<bool> _alive
В конструкторе:
_alive = true;
В деструкторе:
_alive = false;
В прерываемой функции:
void work() {
while (_alive)
do_work();
}
+ Не надо портировать
+ Для пользователя кода очевидны точки прерывания функции
Способы прервать выполнение функции: булев флаг
Булев флаг в нашем примере с worker’ом - это atomic<bool> _alive
В конструкторе:
_alive = true;
В деструкторе:
_alive = false;
В прерываемой функции:
void work() {
while (_alive)
do_work();
}
+ Не надо портировать
+ Для пользователя кода очевидны точки прерывания функции
− Много одинакового кода
− Этот код вне объекта потока
− Мешает декомпозиции
− Ожидание на условных переменных надо прерывать вручную
− Нельзя прервать блокирующие функции (read, write, send, recv, и т.д.)
− Проверку флага легко забыть
Решение - давайте сделаем простой cancellation_token
class cancellation_token {
atomic<bool> _cancelled;
public:
explicit operator bool() const
{ return !_cancelled; }
void cancel()
{ _cancelled = true; }
};
RAII поток? Наконец-то!
class raii_thread {
thread _impl;
cancellation_token _token;
public:
// Function must accept cancellation_token reference as first parameter
template<class Function, class... Args>
raii_thread(Function&& f, Args&&... args)
{ _impl = thread(f, ref(_token), args); }
~raii_thread() {
if (_impl.joinable())
reset();
}
void reset() {
_token.cancel();
_impl.join();
}
// Other raii_thread constructors and methods
};
Улучшенный worker
class worker {
raii_thread _thread;
public:
worker() {
_thread = raii_thread(bind(&worker::work, this, _1));
// Something else, possibly throw
}
~worker()
{ _thread.reset(); }
private:
void work(cancellation_token& token) {
while (token)
do_work();
}
};
Итог: минус один мембер, упрощение деструктора, безопасность в случае исключений
Прерывание ожидания на примитивах
синхронизации
На примере многопоточной очереди
Многопоточная очередь
void concurrent_queue::push(const T& t) {
unique_lock<mutex> l(_mutex);
_queue.push(t);
_condition.notify_one();
}
bool concurrent_queue::try_pop(T& t,
const chrono::milliseconds& timeout) {
unique_lock<mutex> l(_mutex);
if (_queue.empty())
_condition.wait_for(l, timeout);
if (_queue.empty())
return false;
t = _queue.front();
_queue.pop();
return true;
}
Многопоточная очередь
void concurrent_queue::push(const T& t) {
unique_lock<mutex> l(_mutex);
_queue.push(t);
_condition.notify_one();
}
bool concurrent_queue::try_pop(T& t,
const chrono::milliseconds& timeout) {
unique_lock<mutex> l(_mutex);
if (_queue.empty())
_condition.wait_for(l, timeout);
if (_queue.empty())
return false;
t = _queue.front();
_queue.pop();
return true;
}
Памятка по условным переменным
class condition_variable {
// constructors and destructor
void notify_one();
void notify_all();
void wait(unique_lock<mutex>&);
cv_status wait_for(
unique_lock<mutex>&,
std::chrono::duration<...>&);
// other methods
};
Использование многопоточной очереди - task_executor
class task_executor {
concurrent_queue _queue;
raii_thread _thread;
public:
task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); }
~task_executor() { _thread.reset(); }
void add(const function<void()>& f)
{ _queue.push(f); }
private:
void work(cancellation_token& token) {
while (token) {
const chrono::milliseconds timeout = 10; // TODO: Select proper timeout
function<void()> f;
if (_queue.try_pop(f, timeout))
f();
}
}
};
Использование многопоточной очереди - task_executor
class task_executor {
concurrent_queue _queue;
raii_thread _thread;
public:
task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); }
~task_executor() { _thread.reset(); }
void add(const function<void()>& f)
{ _queue.push(f); }
private:
void work(cancellation_token& token) {
while (token) {
const chrono::milliseconds timeout = 100; // TODO: Select proper timeout
function<void()> f;
if (_queue.try_pop(f, timeout))
f();
}
}
};
Использование многопоточной очереди - task_executor
class task_executor {
concurrent_queue _queue;
raii_thread _thread;
public:
task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); }
~task_executor() { _thread.reset(); }
void add(const function<void()>& f)
{ _queue.push(f); }
private:
void work(cancellation_token& token) {
while (token) {
const chrono::milliseconds timeout = 1000; // TODO: Select proper timeout
function<void()> f;
if (_queue.try_pop(f, timeout))
f();
}
}
};
class cancellation_token {
mutex _mutex;
atomic<bool> _cancelled;
cancellation_handler* _handler;
public:
explicit operator bool() const
{ return !_cancelled; }
void cancel() {
unique_lock<mutex> l(_mutex);
if (_handler)
_handler->cancel();
_cancelled = true;
}
// Register/unregister handler impl, etc.
};
struct cancellation_handler {
virtual void cancel() = 0;
};
Продвинутый cancellation_token
Продвинутый cancellation_token
class cancellation_token {
mutex _mutex;
atomic<bool> _cancelled;
cancellation_handler* _handler;
public:
explicit operator bool() const
{ return !_cancelled; }
void cancel() {
unique_lock<mutex> l(_mutex);
if (_handler)
_handler->cancel();
_cancelled = true;
}
// Register/unregister handler impl, etc.
};
struct cancellation_handler {
virtual void cancel() = 0;
};
struct cancellation_guard {
using token = cancellation_token;
using handler = cancellation_handler;
cancellation_guard(token& t, handler& h)
{ t.register_handler(h); }
~cancellation_guard()
{ _t.unregister_handler(); }
// Other methods and fields
};
Условные переменные и cancellation_token
class cv_handler : public cancellation_handler {
condition_variable& _condition;
unique_lock<mutex>& _lock;
public:
cv_handler(condition_variable& c, unique_lock<mutex>& l) : _condition(c), _lock(l)
{ }
virtual void cancel() {
unique_lock l(_lock.get_mutex());
_condition.notify_all();
}
};
void cancellable_wait(condition_variable& cv, unique_lock<mutex>& l, cancellation_token& t)
{
cv_handler handler(cv, l); // implements cancel()
cancellation_guard guard(t, handler); // registers and unregisters handler
cv.wait(l);
}
Многопоточная очередь с cancellation_token
void concurrent_queue::push(const T& t) {
unique_lock<mutex> l(_mutex);
_queue.push(t);
_condition.notify_one();
}
bool concurrent_queue::try_pop(T& t, cancellation_token& token) {
unique_lock<mutex> l(_mutex);
while (token && _queue.empty())
cancellable_wait(_condition, l, token);
if (_queue.empty())
return false;
t = _queue.front();
_queue.pop();
return true;
}
task_executor с cancellation_token
class task_executor {
concurrent_queue _queue;
raii_thread _thread;
public:
task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); }
~task_executor() { _thread.reset(); }
void add(const function<void()>& f)
{ _queue.push(f); }
private:
void work(cancellation_token& token) {
while (token) {
function<void()> f;
if (_queue.try_pop(f, token)) // No more ugly timeouts!
f();
}
}
};
Нужно ли прерывать mutex::lock?
С условными переменными разобрались. Что насчёт мьютекса?
− Мьютексы защищают данные, они не предназначены для ожидания
события
− У мьютекса нет нормального механизма просигналить, что lock() был
прерван
− pthread_cancel никак не влияет на мьютексы - в списке cancellable функций
его нет
Итог: мьютексы мы прерывать не будем.
Прерывание блокирующих функций ОС*
Для случая, где ОС = POSIX
Бестиарий блокирующих функций POSIX
Файловые дескрипторы
ssize_t read(int file_descriptor, void* buffer, size_t bytes_count);
ssize_t write(int file_descriptor, const void* buffer, size_t bytes_count);
Сокеты
ssize_t recv(int socket, void *buffer, size_t length, int flags);
ssize_t send(int socket, const void *buffer, size_t length, int flags);
ssize_t recvmsg(int socket, struct msghdr *message, int flags);
ssize_t sendmsg(int socket, const struct msghdr *message, int flags);
int accept(int socket, struct sockaddr *restrict address, socklen_t *address_len);
int connect(int socket, const struct sockaddr *address, socklen_t address_len);
Бестиарий блокирующих функций POSIX
Файловые дескрипторы
ssize_t read(int file_descriptor, void* buffer, size_t bytes_count); // POLLIN
ssize_t write(int file_descriptor, const void* buffer, size_t bytes_count); // POLLOUT
Сокеты
ssize_t recv(int socket, void *buffer, size_t length, int flags); // POLLIN
ssize_t send(int socket, const void *buffer, size_t length, int flags); // POLLOUT
ssize_t recvmsg(int socket, struct msghdr *message, int flags); // POLLIN
ssize_t sendmsg(int socket, const struct msghdr *message, int flags); // POLLOUT
int accept(int socket, struct sockaddr *restrict address, socklen_t *address_len); // POLLIN
int connect(int socket, const struct sockaddr *address, socklen_t address_len); // POLLOUT
Решение: будем ждать появления данных (места в буфере, подключения, и т.д.)
не в этих вызовах, а в функции poll, которую и будем прерывать.
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
Прерываем poll: poll_cancellation_handler
class poll_cancellation_handler : public cancellation_handler {
int _pipe[2];
public:
poll_cancellation_handler()
{ pipe(_pipe); }
~poll_cancellation_handler()
{ close(_pipe[0]); close(_pipe[1]); }
virtual void cancel() {
char dummy = 0;
write(_pipe[1], &dummy, 1);
}
int get_fd() const
{ return _pipe[0]; }
};
Прерываем всё: cancellable_poll и cancellable_read
short cancellable_poll(int fd, short events, cancellation_token& token) {
poll_cancellation_handler handler;
cancellation_guard guard(token, handler);
pollfd polled_fd = { .fd = fd, .events = events };
pollfd cancel_fd = { .fd = handler.get_fd(), .events = POLLIN };
pollfd fds[2] = { polled_fd, cancel_fd };
poll(fds, 2, -1);
return fds[0].revents;
}
Прерываем всё: cancellable_poll и cancellable_read
short cancellable_poll(int fd, short events, cancellation_token& token) {
poll_cancellation_handler handler;
cancellation_guard guard(token, handler);
pollfd polled_fd = { .fd = fd, .events = events };
pollfd cancel_fd = { .fd = handler.get_fd(), .events = POLLIN };
pollfd fds[2] = { polled_fd, cancel_fd };
poll(fds, 2, -1);
return fds[0].revents;
}
ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count, cancellation_token& token) {
if (cancellable_poll(fd, POLLIN, token) != POLLIN)
return 0; // read was cancelled
return read(fd, buffer, bytes_count);
}
struct pipe_interface {
size_t read(void* buf, size_t size);
size_t write(const void* buf, size_t size);
};
Проектирование интерфейсов с блокирующими функциями
struct pipe_interface {
size_t read(void* buf, size_t size);
size_t write(const void* buf, size_t size);
size_t read(void* buf, size_t size, cancellation_token& t);
size_t write(const void* buf, size_t size, cancellation_token& t);
};
Проектирование интерфейсов с блокирующими функциями
struct pipe_interface {
//size_t read(void* buf, size_t size);
//size_t write(const void* buf, size_t size);
size_t read(void* buf, size_t size, const cancellation_token& t = dummy_token());
size_t write(const void* buf, size_t size, const cancellation_token& t = dummy_token());
};
// cancellation_token.hpp
struct dummy_token : public cancellation_token {
virtual bool is_cancelled() const
{ return false; }
virtual void register(cancellation_handler&) const
{ }
// Unregister handler, etc.
};
Проектирование интерфейсов с блокирующими функциями
struct pipe_interface {
size_t read(void* buf, size_t size, const cancellation_token& t = dummy_token());
size_t write(const void* buf, size_t size, const cancellation_token& t = dummy_token());
};
void thread_func(const cancellation_token& token) {
while (token) {
size_t s = _pipe.read(_buffer.data(), _buffer.size(), token);
if (s != 0)
handle_data(_buffer.data(), s);
else if (token)
handle_eof();
}
}
Проектирование интерфейсов с блокирующими функциями
Результаты
cancellation_token
Объект, ссылка на который явно передаётся во все длительные вызовы в данном потоке. Позволяет
узнать, был ли прерван данный поток. Можно зарегистрировать обработчик, который реализует
произвольный механизм прерывания функции.
cancellation_token
Объект, ссылка на который явно передаётся во все длительные вызовы в данном потоке. Позволяет
узнать, был ли прерван данный поток. Можно зарегистрировать обработчик, который реализует
произвольный механизм прерывания функции.
+ Для пользователя кода очевидны точки, в которых выполнение функции может быть
остановлено
+ Можно прервать ожидание на условных переменных
+ Можно прервать большинство блокирующих функций (read, write, send, recv, и т.д.)
+ Поддержка пользовательских механизмов прерывания функций
+ Упрощает декомпозицию объектов с длительными вызовами
+ Легко портировать - от платформы зависит только механизм прерывания системных вызовов
+ Можно прерывать отдельные задачи, а не потоки целиком
− Проверку токена можно забыть
− Накладные расходы на прерывание системных вызовов
Результаты
Отвергнутые альтернативы
cancelled_exception
ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count, cancellation_token& token) {
if (cancellable_poll(fd, POLLIN, token) != POLLIN)
throw cancelled_exception("Read was cancelled!");
return read(fd, buffer, bytes_count);
}
cancelled_exception
ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count, cancellation_token& token) {
if (cancellable_poll(fd, POLLIN, token) != POLLIN)
throw cancelled_exception("Read was cancelled!");
return read(fd, buffer, bytes_count);
}
+ Сложнее проигнорировать
+ Нет трудностей с возвращаемыми значениями
− Снижение гибкости
− Нарушение философии исключений
− Усложнение отладки
Отвергнутые альтернативы
Неявная передача cancellation_token через thread-local storage
ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count) {
if (cancellable_poll(fd, POLLIN, get_token_from_tls()) != POLLIN)
return 0; // read was cancelled
return read(fd, buffer, bytes_count);
}
Отвергнутые альтернативы
Неявная передача cancellation_token через thread-local storage
ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count) {
if (cancellable_poll(fd, POLLIN, get_token_from_tls()) != POLLIN)
return 0; // read was cancelled
return read(fd, buffer, bytes_count);
}
+ Не надо передавать дополнительный аргумент
+ Легче добавить cancellation_token в существующий код
− Неочевидность
− Снижение гибкости
− Необходимо два набора методов - прерываемый и не прерываемый
− Сложности с прерыванием отдельной задачи, а не потока целиком
Отвергнутые альтернативы
https://github.com/bo-on-software/rethread
bsazonov@gmail.com
Вопросы?
Спасибо за внимание!

Contenu connexe

Tendances

Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о BoostSergey Platonov
 
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...Sergey Platonov
 
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptСергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptSergey Platonov
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey TeplyakovAlex Tumanoff
 
Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияЕвгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияPlatonov Sergey
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAlex Tumanoff
 
хитрости выведения типов
хитрости выведения типовхитрости выведения типов
хитрости выведения типовcorehard_by
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and catscorehard_by
 
Как за час сделать недельную работу
Как за час сделать недельную работуКак за час сделать недельную работу
Как за час сделать недельную работуcorehard_by
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Platonov Sergey
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerAnton Arhipov
 
Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Yauheni Akhotnikau
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиvictor-yastrebov
 
JPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчикаJPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчикаAnton Arhipov
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Dima Dzuba
 
Как сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееКак сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееRoman Dvornov
 

Tendances (20)

Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о Boost
 
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
 
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptСергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey Teplyakov
 
Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияЕвгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализация
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey Teplyakov
 
хитрости выведения типов
хитрости выведения типовхитрости выведения типов
хитрости выведения типов
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and cats
 
Как за час сделать недельную работу
Как за час сделать недельную работуКак за час сделать недельную работу
Как за час сделать недельную работу
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
 
Parallel STL
Parallel STLParallel STL
Parallel STL
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
 
Zagursky
ZagurskyZagursky
Zagursky
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибки
 
Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработки
 
JPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчикаJPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчика
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6
 
Как сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееКак сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрее
 

Similaire à Борис Сазонов, RAII потоки и CancellationToken в C++

Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозитElena Kotina
 
Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Vladimir Kochetkov
 
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
Ошибки, которые сложно заметить на code review, но которые находятся статичес...Ошибки, которые сложно заметить на code review, но которые находятся статичес...
Ошибки, которые сложно заметить на code review, но которые находятся статичес...Andrey Karpov
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlOleksandr Petrov
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonovComputer Science Club
 
C++ осень 2012 лекция 9
C++ осень 2012 лекция 9C++ осень 2012 лекция 9
C++ осень 2012 лекция 9Technopark
 
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в TarantoolИнструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в TarantoolTimur Safin
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)Alexander Gornik
 
PHP Tricks
PHP TricksPHP Tricks
PHP TricksBlackFan
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...Alexey Paznikov
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Andrey Karpov
 
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...OdessaFrontend
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksMikhail Kurnosov
 
Lecture5
Lecture5Lecture5
Lecture5orgil
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?Vasil Remeniuk
 

Similaire à Борис Сазонов, RAII потоки и CancellationToken в C++ (20)

Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозит
 
Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!
 
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
Ошибки, которые сложно заметить на code review, но которые находятся статичес...Ошибки, которые сложно заметить на code review, но которые находятся статичес...
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher Sql
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
 
C++ осень 2012 лекция 9
C++ осень 2012 лекция 9C++ осень 2012 лекция 9
C++ осень 2012 лекция 9
 
JavaDay'14
JavaDay'14JavaDay'14
JavaDay'14
 
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в TarantoolИнструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в Tarantool
 
Clojure #2 (2014)
Clojure #2 (2014)Clojure #2 (2014)
Clojure #2 (2014)
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)
 
UWDC 2013, Yii2
UWDC 2013, Yii2UWDC 2013, Yii2
UWDC 2013, Yii2
 
PHP Tricks
PHP TricksPHP Tricks
PHP Tricks
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?
 
Bytecode
BytecodeBytecode
Bytecode
 
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
Lecture5
Lecture5Lecture5
Lecture5
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
 

Plus de Sergey Platonov

Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерSergey Platonov
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++NextSergey Platonov
 
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against itEvgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against itSergey Platonov
 
Василий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексиейВасилий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексиейSergey Platonov
 
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного багаЛев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного багаSergey Platonov
 
Антон Бикинеев, Writing good std::future&lt; C++ >
Антон Бикинеев, Writing good std::future&lt; C++ >Антон Бикинеев, Writing good std::future&lt; C++ >
Антон Бикинеев, Writing good std::future&lt; C++ >Sergey Platonov
 
Павел Филонов, Разделяй и управляй вместе с Conan.io
Павел Филонов, Разделяй и управляй вместе с Conan.ioПавел Филонов, Разделяй и управляй вместе с Conan.io
Павел Филонов, Разделяй и управляй вместе с Conan.ioSergey Platonov
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияSergey Platonov
 
Антон Полухин. C++17
Антон Полухин. C++17Антон Полухин. C++17
Антон Полухин. C++17Sergey Platonov
 
Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Sergey Platonov
 
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на QtДенис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на QtSergey Platonov
 
Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereSergey Platonov
 
Дмитрий Нестерук, Паттерны проектирования в XXI веке
Дмитрий Нестерук, Паттерны проектирования в XXI векеДмитрий Нестерук, Паттерны проектирования в XXI веке
Дмитрий Нестерук, Паттерны проектирования в XXI векеSergey Platonov
 
Dori Exterman, Considerations for choosing the parallel computing strategy th...
Dori Exterman, Considerations for choosing the parallel computing strategy th...Dori Exterman, Considerations for choosing the parallel computing strategy th...
Dori Exterman, Considerations for choosing the parallel computing strategy th...Sergey Platonov
 
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Sergey Platonov
 
Антон Нонко, Классические строки в C++
Антон Нонко, Классические строки в C++Антон Нонко, Классические строки в C++
Антон Нонко, Классические строки в C++Sergey Platonov
 
Михаил Матросов, Повседневный С++: boost и STL
Михаил Матросов, Повседневный С++: boost и STLМихаил Матросов, Повседневный С++: boost и STL
Михаил Матросов, Повседневный С++: boost и STLSergey Platonov
 
Алексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуляАлексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуляSergey Platonov
 
Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++Sergey Platonov
 
Дмитрий Демчук. Кроссплатформенный краш-репорт
Дмитрий Демчук. Кроссплатформенный краш-репортДмитрий Демчук. Кроссплатформенный краш-репорт
Дмитрий Демчук. Кроссплатформенный краш-репортSergey Platonov
 

Plus de Sergey Platonov (20)

Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++Next
 
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against itEvgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
 
Василий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексиейВасилий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексией
 
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного багаЛев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
Лев Казаркин, Удивительные приключения регистров SSE или в поисках одного бага
 
Антон Бикинеев, Writing good std::future&lt; C++ >
Антон Бикинеев, Writing good std::future&lt; C++ >Антон Бикинеев, Writing good std::future&lt; C++ >
Антон Бикинеев, Writing good std::future&lt; C++ >
 
Павел Филонов, Разделяй и управляй вместе с Conan.io
Павел Филонов, Разделяй и управляй вместе с Conan.ioПавел Филонов, Разделяй и управляй вместе с Conan.io
Павел Филонов, Разделяй и управляй вместе с Conan.io
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизация
 
Антон Полухин. C++17
Антон Полухин. C++17Антон Полухин. C++17
Антон Полухин. C++17
 
Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++
 
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на QtДенис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
Денис Кандров, Пушкова Евгения, QSpec: тестирование графических приложений на Qt
 
Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhere
 
Дмитрий Нестерук, Паттерны проектирования в XXI веке
Дмитрий Нестерук, Паттерны проектирования в XXI векеДмитрий Нестерук, Паттерны проектирования в XXI веке
Дмитрий Нестерук, Паттерны проектирования в XXI веке
 
Dori Exterman, Considerations for choosing the parallel computing strategy th...
Dori Exterman, Considerations for choosing the parallel computing strategy th...Dori Exterman, Considerations for choosing the parallel computing strategy th...
Dori Exterman, Considerations for choosing the parallel computing strategy th...
 
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
 
Антон Нонко, Классические строки в C++
Антон Нонко, Классические строки в C++Антон Нонко, Классические строки в C++
Антон Нонко, Классические строки в C++
 
Михаил Матросов, Повседневный С++: boost и STL
Михаил Матросов, Повседневный С++: boost и STLМихаил Матросов, Повседневный С++: boost и STL
Михаил Матросов, Повседневный С++: boost и STL
 
Алексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуляАлексей Кутумов, Вектор с нуля
Алексей Кутумов, Вектор с нуля
 
Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++
 
Дмитрий Демчук. Кроссплатформенный краш-репорт
Дмитрий Демчук. Кроссплатформенный краш-репортДмитрий Демчук. Кроссплатформенный краш-репорт
Дмитрий Демчук. Кроссплатформенный краш-репорт
 

Борис Сазонов, RAII потоки и CancellationToken в C++

  • 1. RAII потоки и cancellation_token в C++ Борис Сазонов, 2016
  • 2. Disclaimer В этой презентации вы встретите: ✓ Извращения в форматировании кода ✓ C-style комментарии ✓ Чересчур лаконичные имена классов ✓ Неэффективный код без использования move и forward ✓ Отсутствие проверки ошибок ✓ Невидимый “using namespace std” ✓ Прочие гадости Всё это сделано для того, чтобы сохранить разумный размер шрифта. Всегда пожалуйста.
  • 3. Problem, officer? class worker { atomic<bool> _alive; thread _thread; public: worker() : _alive(true) { _thread = thread(bind(&worker::work, this)); // Something else... } ~worker() { _alive = false; _thread.join(); } private: void work() { while (_alive) do_work(); } };
  • 4. Problem, officer? class worker { atomic<bool> _alive; thread _thread; public: worker() : _alive(true) { _thread = thread(bind(&worker::work, this)); throw runtime_error("Something can throw!"); } ~worker() { _alive = false; _thread.join(); } private: void work() { while (_alive) do_work(); } };
  • 5. Bang! class worker { atomic<bool> _alive; thread _thread; public: worker() : _alive(true) { _thread = thread(bind(&worker::work, this)); throw runtime_error("Something can throw!"); // Bang! Calls terminate() } ~worker() { _alive = false; _thread.join(); } private: void work() { while (_alive) do_work(); } };
  • 6. Зловещий деструктор 30.3.1.3 thread destructor ~thread(); If joinable() then terminate(), otherwise no effects.
  • 7. Зловещий деструктор 30.3.1.3 thread destructor ~thread(); If joinable() then terminate(), otherwise no effects. Попробуем что-нибудь с этим сделать: class raii_thread { thread _impl; public: // raii_thread constructors and methods ~raii_thread() { if (_impl.joinable()) reset(); } void reset() { /* ??? */ } };
  • 8. void reset() { _impl.detach(); } Спасение от зловещего деструктора: detach vs. join
  • 9. Спасение от зловещего деструктора: detach vs. join void reset() { _impl.detach(); } class worker { atomic<bool> _alive; raii_thread _thread; public: worker() : _alive(true) { _thread = raii_thread(bind(&worker::work, this)); // Something else, possibly throw } ~worker() { _alive = false; _thread.reset(); } private: void work() { while (_alive) do_work(); } };
  • 10. Спасение от зловещего деструктора: detach vs. join void reset() { _impl.detach(); } − Небезопасно - в любой непонятной ситуации поток будет работать с мёртвым объектом − Нарушение RAII − Личная неприязнь class worker { atomic<bool> _alive; raii_thread _thread; public: worker() : _alive(true) { _thread = raii_thread(bind(&worker::work, this)); // Something else, possibly throw } ~worker() { _alive = false; _thread.reset(); } private: void work() { while (_alive) do_work(); } };
  • 11. Спасение от зловещего деструктора: detach vs. join void reset() { _impl.detach(); } − Небезопасно - в любой непонятной ситуации поток будет работать с мёртвым объектом − Нарушение RAII − Личная неприязнь void reset() { _impl.join(); } class worker { atomic<bool> _alive; raii_thread _thread; public: worker() : _alive(true) { _thread = raii_thread(bind(&worker::work, this)); // Something else, possibly throw } ~worker() { _alive = false; _thread.reset(); } private: void work() { while (_alive) do_work(); } };
  • 12. Спасение от зловещего деструктора: detach vs. join void reset() { _impl.detach(); } − Небезопасно - в любой непонятной ситуации поток будет работать с мёртвым объектом − Нарушение RAII − Личная неприязнь void reset() { _impl.join(); } + RAII über alles − Отсутствие у потока возможности завершить исполняемую функцию, что приводит к зависанию в деструкторе class worker { atomic<bool> _alive; raii_thread _thread; public: worker() : _alive(true) { _thread = raii_thread(bind(&worker::work, this)); // Something else, possibly throw } ~worker() { _alive = false; _thread.reset(); } private: void work() { while (_alive) do_work(); } };
  • 13. Interrupt (pthread_cancel, boost::thread::interrupt и т.д.) Потоку можно отправить запрос на прерывание исполнения. Тогда в целевом потоке из системных вызовов (read, write, и т.д.) вылетит исключение специального типа. Ещё есть специальная функция, который позволяет проверить, не был ли прерван текущий поток (pthread_testcancel, boost::thread:: interruption_point, и т.д.). Способы прервать выполнение функции: Interrupt
  • 14. Interrupt (pthread_cancel, boost::thread::interrupt и т.д.) Потоку можно отправить запрос на прерывание исполнения. Тогда в целевом потоке из системных вызовов (read, write, и т.д.) вылетит исключение специального типа. Ещё есть специальная функция, который позволяет проверить, не был ли прерван текущий поток (pthread_testcancel, boost::thread:: interruption_point, и т.д.). + Прерывает ожидание на условных переменных + Прерывает блокирующие функции ОС (read, write, send, recv, и т.д.) + Практически невозможно игнорировать Способы прервать выполнение функции: Interrupt
  • 15. Interrupt (pthread_cancel, boost::thread::interrupt и т.д.) Потоку можно отправить запрос на прерывание исполнения. Тогда в целевом потоке из системных вызовов (read, write, и т.д.) вылетит исключение специального типа. Ещё есть специальная функция, который позволяет проверить, не был ли прерван текущий поток (pthread_testcancel, boost::thread:: interruption_point, и т.д.). + Прерывает ожидание на условных переменных + Прерывает блокирующие функции ОС (read, write, send, recv, и т.д.) + Практически невозможно игнорировать − Исключение из системных вызовов станет сюрпризом для многих библиотек, написанных на C. Вероятный результат - утечка ресурсов, не разблокированные мьютексы и т.д. − Cистемные вызовы в деструкторах могут кинуть исключение − Сложности с портированием - на многих ОС pthread_cancel или аналогов нет (и не будет) − C++ STL нет interrupt или аналога − В C++14 condition_variable::wait не кидает исключений − Необратимость - нельзя переиспользовать поток Способы прервать выполнение функции: Interrupt
  • 16. Способы прервать выполнение функции: булев флаг Булев флаг в нашем примере с worker’ом - это atomic<bool> _alive В конструкторе: _alive = true; В деструкторе: _alive = false; В прерываемой функции: void work() { while (_alive) do_work(); }
  • 17. Способы прервать выполнение функции: булев флаг Булев флаг в нашем примере с worker’ом - это atomic<bool> _alive В конструкторе: _alive = true; В деструкторе: _alive = false; В прерываемой функции: void work() { while (_alive) do_work(); } + Не надо портировать + Для пользователя кода очевидны точки прерывания функции
  • 18. Способы прервать выполнение функции: булев флаг Булев флаг в нашем примере с worker’ом - это atomic<bool> _alive В конструкторе: _alive = true; В деструкторе: _alive = false; В прерываемой функции: void work() { while (_alive) do_work(); } + Не надо портировать + Для пользователя кода очевидны точки прерывания функции − Много одинакового кода − Этот код вне объекта потока − Мешает декомпозиции − Ожидание на условных переменных надо прерывать вручную − Нельзя прервать блокирующие функции (read, write, send, recv, и т.д.) − Проверку флага легко забыть
  • 19. Решение - давайте сделаем простой cancellation_token class cancellation_token { atomic<bool> _cancelled; public: explicit operator bool() const { return !_cancelled; } void cancel() { _cancelled = true; } };
  • 20. RAII поток? Наконец-то! class raii_thread { thread _impl; cancellation_token _token; public: // Function must accept cancellation_token reference as first parameter template<class Function, class... Args> raii_thread(Function&& f, Args&&... args) { _impl = thread(f, ref(_token), args); } ~raii_thread() { if (_impl.joinable()) reset(); } void reset() { _token.cancel(); _impl.join(); } // Other raii_thread constructors and methods };
  • 21. Улучшенный worker class worker { raii_thread _thread; public: worker() { _thread = raii_thread(bind(&worker::work, this, _1)); // Something else, possibly throw } ~worker() { _thread.reset(); } private: void work(cancellation_token& token) { while (token) do_work(); } }; Итог: минус один мембер, упрощение деструктора, безопасность в случае исключений
  • 22. Прерывание ожидания на примитивах синхронизации На примере многопоточной очереди
  • 23. Многопоточная очередь void concurrent_queue::push(const T& t) { unique_lock<mutex> l(_mutex); _queue.push(t); _condition.notify_one(); } bool concurrent_queue::try_pop(T& t, const chrono::milliseconds& timeout) { unique_lock<mutex> l(_mutex); if (_queue.empty()) _condition.wait_for(l, timeout); if (_queue.empty()) return false; t = _queue.front(); _queue.pop(); return true; }
  • 24. Многопоточная очередь void concurrent_queue::push(const T& t) { unique_lock<mutex> l(_mutex); _queue.push(t); _condition.notify_one(); } bool concurrent_queue::try_pop(T& t, const chrono::milliseconds& timeout) { unique_lock<mutex> l(_mutex); if (_queue.empty()) _condition.wait_for(l, timeout); if (_queue.empty()) return false; t = _queue.front(); _queue.pop(); return true; } Памятка по условным переменным class condition_variable { // constructors and destructor void notify_one(); void notify_all(); void wait(unique_lock<mutex>&); cv_status wait_for( unique_lock<mutex>&, std::chrono::duration<...>&); // other methods };
  • 25. Использование многопоточной очереди - task_executor class task_executor { concurrent_queue _queue; raii_thread _thread; public: task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); } ~task_executor() { _thread.reset(); } void add(const function<void()>& f) { _queue.push(f); } private: void work(cancellation_token& token) { while (token) { const chrono::milliseconds timeout = 10; // TODO: Select proper timeout function<void()> f; if (_queue.try_pop(f, timeout)) f(); } } };
  • 26. Использование многопоточной очереди - task_executor class task_executor { concurrent_queue _queue; raii_thread _thread; public: task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); } ~task_executor() { _thread.reset(); } void add(const function<void()>& f) { _queue.push(f); } private: void work(cancellation_token& token) { while (token) { const chrono::milliseconds timeout = 100; // TODO: Select proper timeout function<void()> f; if (_queue.try_pop(f, timeout)) f(); } } };
  • 27. Использование многопоточной очереди - task_executor class task_executor { concurrent_queue _queue; raii_thread _thread; public: task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); } ~task_executor() { _thread.reset(); } void add(const function<void()>& f) { _queue.push(f); } private: void work(cancellation_token& token) { while (token) { const chrono::milliseconds timeout = 1000; // TODO: Select proper timeout function<void()> f; if (_queue.try_pop(f, timeout)) f(); } } };
  • 28. class cancellation_token { mutex _mutex; atomic<bool> _cancelled; cancellation_handler* _handler; public: explicit operator bool() const { return !_cancelled; } void cancel() { unique_lock<mutex> l(_mutex); if (_handler) _handler->cancel(); _cancelled = true; } // Register/unregister handler impl, etc. }; struct cancellation_handler { virtual void cancel() = 0; }; Продвинутый cancellation_token
  • 29. Продвинутый cancellation_token class cancellation_token { mutex _mutex; atomic<bool> _cancelled; cancellation_handler* _handler; public: explicit operator bool() const { return !_cancelled; } void cancel() { unique_lock<mutex> l(_mutex); if (_handler) _handler->cancel(); _cancelled = true; } // Register/unregister handler impl, etc. }; struct cancellation_handler { virtual void cancel() = 0; }; struct cancellation_guard { using token = cancellation_token; using handler = cancellation_handler; cancellation_guard(token& t, handler& h) { t.register_handler(h); } ~cancellation_guard() { _t.unregister_handler(); } // Other methods and fields };
  • 30. Условные переменные и cancellation_token class cv_handler : public cancellation_handler { condition_variable& _condition; unique_lock<mutex>& _lock; public: cv_handler(condition_variable& c, unique_lock<mutex>& l) : _condition(c), _lock(l) { } virtual void cancel() { unique_lock l(_lock.get_mutex()); _condition.notify_all(); } }; void cancellable_wait(condition_variable& cv, unique_lock<mutex>& l, cancellation_token& t) { cv_handler handler(cv, l); // implements cancel() cancellation_guard guard(t, handler); // registers and unregisters handler cv.wait(l); }
  • 31. Многопоточная очередь с cancellation_token void concurrent_queue::push(const T& t) { unique_lock<mutex> l(_mutex); _queue.push(t); _condition.notify_one(); } bool concurrent_queue::try_pop(T& t, cancellation_token& token) { unique_lock<mutex> l(_mutex); while (token && _queue.empty()) cancellable_wait(_condition, l, token); if (_queue.empty()) return false; t = _queue.front(); _queue.pop(); return true; }
  • 32. task_executor с cancellation_token class task_executor { concurrent_queue _queue; raii_thread _thread; public: task_executor() { _thread = raii_thread(bind(&task_executor::work, this, _1)); } ~task_executor() { _thread.reset(); } void add(const function<void()>& f) { _queue.push(f); } private: void work(cancellation_token& token) { while (token) { function<void()> f; if (_queue.try_pop(f, token)) // No more ugly timeouts! f(); } } };
  • 33. Нужно ли прерывать mutex::lock? С условными переменными разобрались. Что насчёт мьютекса? − Мьютексы защищают данные, они не предназначены для ожидания события − У мьютекса нет нормального механизма просигналить, что lock() был прерван − pthread_cancel никак не влияет на мьютексы - в списке cancellable функций его нет Итог: мьютексы мы прерывать не будем.
  • 34. Прерывание блокирующих функций ОС* Для случая, где ОС = POSIX
  • 35. Бестиарий блокирующих функций POSIX Файловые дескрипторы ssize_t read(int file_descriptor, void* buffer, size_t bytes_count); ssize_t write(int file_descriptor, const void* buffer, size_t bytes_count); Сокеты ssize_t recv(int socket, void *buffer, size_t length, int flags); ssize_t send(int socket, const void *buffer, size_t length, int flags); ssize_t recvmsg(int socket, struct msghdr *message, int flags); ssize_t sendmsg(int socket, const struct msghdr *message, int flags); int accept(int socket, struct sockaddr *restrict address, socklen_t *address_len); int connect(int socket, const struct sockaddr *address, socklen_t address_len);
  • 36. Бестиарий блокирующих функций POSIX Файловые дескрипторы ssize_t read(int file_descriptor, void* buffer, size_t bytes_count); // POLLIN ssize_t write(int file_descriptor, const void* buffer, size_t bytes_count); // POLLOUT Сокеты ssize_t recv(int socket, void *buffer, size_t length, int flags); // POLLIN ssize_t send(int socket, const void *buffer, size_t length, int flags); // POLLOUT ssize_t recvmsg(int socket, struct msghdr *message, int flags); // POLLIN ssize_t sendmsg(int socket, const struct msghdr *message, int flags); // POLLOUT int accept(int socket, struct sockaddr *restrict address, socklen_t *address_len); // POLLIN int connect(int socket, const struct sockaddr *address, socklen_t address_len); // POLLOUT Решение: будем ждать появления данных (места в буфере, подключения, и т.д.) не в этих вызовах, а в функции poll, которую и будем прерывать. int poll(struct pollfd fds[], nfds_t nfds, int timeout);
  • 37. Прерываем poll: poll_cancellation_handler class poll_cancellation_handler : public cancellation_handler { int _pipe[2]; public: poll_cancellation_handler() { pipe(_pipe); } ~poll_cancellation_handler() { close(_pipe[0]); close(_pipe[1]); } virtual void cancel() { char dummy = 0; write(_pipe[1], &dummy, 1); } int get_fd() const { return _pipe[0]; } };
  • 38. Прерываем всё: cancellable_poll и cancellable_read short cancellable_poll(int fd, short events, cancellation_token& token) { poll_cancellation_handler handler; cancellation_guard guard(token, handler); pollfd polled_fd = { .fd = fd, .events = events }; pollfd cancel_fd = { .fd = handler.get_fd(), .events = POLLIN }; pollfd fds[2] = { polled_fd, cancel_fd }; poll(fds, 2, -1); return fds[0].revents; }
  • 39. Прерываем всё: cancellable_poll и cancellable_read short cancellable_poll(int fd, short events, cancellation_token& token) { poll_cancellation_handler handler; cancellation_guard guard(token, handler); pollfd polled_fd = { .fd = fd, .events = events }; pollfd cancel_fd = { .fd = handler.get_fd(), .events = POLLIN }; pollfd fds[2] = { polled_fd, cancel_fd }; poll(fds, 2, -1); return fds[0].revents; } ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count, cancellation_token& token) { if (cancellable_poll(fd, POLLIN, token) != POLLIN) return 0; // read was cancelled return read(fd, buffer, bytes_count); }
  • 40. struct pipe_interface { size_t read(void* buf, size_t size); size_t write(const void* buf, size_t size); }; Проектирование интерфейсов с блокирующими функциями
  • 41. struct pipe_interface { size_t read(void* buf, size_t size); size_t write(const void* buf, size_t size); size_t read(void* buf, size_t size, cancellation_token& t); size_t write(const void* buf, size_t size, cancellation_token& t); }; Проектирование интерфейсов с блокирующими функциями
  • 42. struct pipe_interface { //size_t read(void* buf, size_t size); //size_t write(const void* buf, size_t size); size_t read(void* buf, size_t size, const cancellation_token& t = dummy_token()); size_t write(const void* buf, size_t size, const cancellation_token& t = dummy_token()); }; // cancellation_token.hpp struct dummy_token : public cancellation_token { virtual bool is_cancelled() const { return false; } virtual void register(cancellation_handler&) const { } // Unregister handler, etc. }; Проектирование интерфейсов с блокирующими функциями
  • 43. struct pipe_interface { size_t read(void* buf, size_t size, const cancellation_token& t = dummy_token()); size_t write(const void* buf, size_t size, const cancellation_token& t = dummy_token()); }; void thread_func(const cancellation_token& token) { while (token) { size_t s = _pipe.read(_buffer.data(), _buffer.size(), token); if (s != 0) handle_data(_buffer.data(), s); else if (token) handle_eof(); } } Проектирование интерфейсов с блокирующими функциями
  • 44. Результаты cancellation_token Объект, ссылка на который явно передаётся во все длительные вызовы в данном потоке. Позволяет узнать, был ли прерван данный поток. Можно зарегистрировать обработчик, который реализует произвольный механизм прерывания функции.
  • 45. cancellation_token Объект, ссылка на который явно передаётся во все длительные вызовы в данном потоке. Позволяет узнать, был ли прерван данный поток. Можно зарегистрировать обработчик, который реализует произвольный механизм прерывания функции. + Для пользователя кода очевидны точки, в которых выполнение функции может быть остановлено + Можно прервать ожидание на условных переменных + Можно прервать большинство блокирующих функций (read, write, send, recv, и т.д.) + Поддержка пользовательских механизмов прерывания функций + Упрощает декомпозицию объектов с длительными вызовами + Легко портировать - от платформы зависит только механизм прерывания системных вызовов + Можно прерывать отдельные задачи, а не потоки целиком − Проверку токена можно забыть − Накладные расходы на прерывание системных вызовов Результаты
  • 46. Отвергнутые альтернативы cancelled_exception ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count, cancellation_token& token) { if (cancellable_poll(fd, POLLIN, token) != POLLIN) throw cancelled_exception("Read was cancelled!"); return read(fd, buffer, bytes_count); }
  • 47. cancelled_exception ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count, cancellation_token& token) { if (cancellable_poll(fd, POLLIN, token) != POLLIN) throw cancelled_exception("Read was cancelled!"); return read(fd, buffer, bytes_count); } + Сложнее проигнорировать + Нет трудностей с возвращаемыми значениями − Снижение гибкости − Нарушение философии исключений − Усложнение отладки Отвергнутые альтернативы
  • 48. Неявная передача cancellation_token через thread-local storage ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count) { if (cancellable_poll(fd, POLLIN, get_token_from_tls()) != POLLIN) return 0; // read was cancelled return read(fd, buffer, bytes_count); } Отвергнутые альтернативы
  • 49. Неявная передача cancellation_token через thread-local storage ssize_t cancellable_read(int fd, void* buffer, size_t bytes_count) { if (cancellable_poll(fd, POLLIN, get_token_from_tls()) != POLLIN) return 0; // read was cancelled return read(fd, buffer, bytes_count); } + Не надо передавать дополнительный аргумент + Легче добавить cancellation_token в существующий код − Неочевидность − Снижение гибкости − Необходимо два набора методов - прерываемый и не прерываемый − Сложности с прерыванием отдельной задачи, а не потока целиком Отвергнутые альтернативы