Я пытаюсь безопасно отменить boost::asio::basic_waitable_timer<std::chrono::steady_clock>
.
Согласно этому ответу, этот код должен выполнять эту работу:
timer.get_io_service().post([&]{timer.cancel();})
Боюсь, у меня это не работает.
Я что-то не так делаю?
Вот мой код:
#include <iostream>
#include "boost/asio.hpp"
#include <chrono>
#include <thread>
#include <random>
boost::asio::io_service io_service;
boost::asio::basic_waitable_timer<std::chrono::steady_clock> timer(io_service);
std::atomic<bool> started;
void handle_timeout(const boost::system::error_code& ec)
{
if (!ec) {
started = true;
std::cerr << "tid: " << std::this_thread::get_id() << ", handle_timeout\n";
timer.expires_from_now(std::chrono::milliseconds(10));
timer.async_wait(&handle_timeout);
} else if (ec == boost::asio::error::operation_aborted) {
std::cerr << "tid: " << std::this_thread::get_id() << ", handle_timeout aborted\n";
} else {
std::cerr << "tid: " << std::this_thread::get_id() << ", handle_timeout another error\n";
}
}
int main() {
std::cout << "tid: " << std::this_thread::get_id() << ", Hello, World!" << std::endl;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(1, 100);
for (auto i = 0; i < 1000; i++) {
started = false;
std::thread t([&](){
timer.expires_from_now(std::chrono::milliseconds(0));
timer.async_wait(&handle_timeout);
io_service.run();
});
while (!started) {};
auto sleep = dis(gen);
std::cout << "tid: " << std::this_thread::get_id() << ", i: " << i << ", sleeps for " << sleep << " [ms]" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
timer.get_io_service().post([](){
std::cerr << "tid: " << std::this_thread::get_id() << ", cancelling in post\n";
timer.cancel();
});
// timer.cancel();
std::cout << "tid: " << std::this_thread::get_id() << ", i: " << i << ", waiting for thread to join()" << std::endl;
t.join();
io_service.reset();
}
return 0;
}
Это результат:
...
tid: 140737335076608, handle_timeout
tid: 140737335076608, handle_timeout
tid: 140737353967488, i: 2, ожидание объединения потока()
tid: 140737335076608, отмена в сообщении
tid: 140737335076608, handle_timeout aborted
tid: 140737353967488, i: 3, засыпает на 21 [мс]
tid: 140737335076608, handle_timeout
tid: 140737353967488, i: 3, ожидание объединения потока BR> TID: 140737335076608, Handle_timeout
TID: 140737335076608, Отмена в Post
TID: 140737335076608, Handle_timeout
TID: 140737335076608, Handle_timeout
TID: 140737335076608, Handle_timeout
TID: 140737335076608, Handle_timeout < br> tid: 140737335076608, handle_timeout
...
продолжаться вечно...
Как видите, timer.cancel()
вызывается из соответствующего потока:
tid: 140737335076608, отмена по почте
НО нет
tid: 140737335076608, handle_timeout прерван
После.
Главное ждет вечно.
The cancellation is safe.
. Вы имели в виду использованиеpost()
, верно? Не обычныйtimer.cancel()
? 02.04.2017close()
, как я описал. Нетрудно написать один, объединяющий таймеры Asio. На практике у меня обычно есть отдельный флаг отключения/счетчик ссылок, чтобы у меня не было этой проблемы. 02.04.2017cancel()
является потокобезопасным именно из-за его реализации? Когда я пишу функциюstop()
, которая будет останавливать такие вещи, какdeadline_timer
/socket
/stream descriptor
/signal
, должен ли я использоватьpost()
в качестве эмпирического правила, чтобы предотвратить неопределенное поведение, когда разные потоки вызывают этотstop()
или все эти вызовы должен быть потокобезопасным, какcancel()
? Спасибо 03.04.2017deadline_timer
). См. stackoverflow.com/questions/12794107/. Я надеюсь, что это доказывает, чтоcancel()
не является потокобезопасным, согласно документации (никто не говорил об этом). 03.04.2017boost::asio::signal_set
? Или я должен использовать какой-то флагshutdown
? 03.04.2017signal_set.clear(...);
(теперь, когда вы получаете сигнал 0, это означает, что вам, вероятно, следует завершить работу) 03.04.2017