Как использовать boost::asio::async_result, почему мой код вылетает из-за (прервано сигналом 11: SIGSEGV)
using ReadSignature = void(int);
template <class CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken , ReadSignature)
AsyncRead(boost::asio::io_service* ios,CompletionToken&& token) {
using Handler = typename boost::asio::handler_type<CompletionToken,
ReadSignature>::type;
Handler handler(std::forward<CompletionToken>(token));
boost::asio::async_result<Handler> result(handler);
std::cout << std::time(nullptr) << ":before thread" << std::endl;
std::thread thread([ios,&handler]() {
std::cout << std::time(nullptr) << ":run in thread before sleep"<< std::endl;
sleep(5);
std::cout << std::time(nullptr) << ":run in thread after sleep"<< std::endl;
std::cout << std::time(nullptr) << ":run in thread before cb" << std::endl;
ios->post([&handler](){
handler(2);
});
std::cout << std::time(nullptr) << ":run in thread after cb"<<std::endl;
});
thread.detach();
return result.get();
}
int main(int argc, char** argv) {
boost::asio::io_service s;
boost::asio::io_service::work worker(s);
boost::asio::spawn(s,[&s](boost::asio::yield_context yield){
boost::system::error_code er;
int val=AsyncRead(&s,yield[er]);
std::cout << std::time(nullptr) <<"get:"<<val<< "" << std::endl;
});
s.run();
return 0;
}
Я ожидаю: вернуть результат.get(); даст волокно, и значение 2 получит. но сбой кода с: (прервано сигналом 11: SIGSEGV), потому что handler.ec равен нулю.
Этим
ios->post([&handler](){
handler(2);
});
вы создаете лямбду, которая ставится в очередь в io_service
. Это закрытие выполняется внутри io_service::run
. Вы захватываете по ссылке handler
, которая является локальной внутри AsyncRead
.
Когда handler(2)
вызывается
AsyncRead(&s,yield[er]);
сопрограмма возобновляется в строке выше, result.get()
вызывается из AsyncRead
, AsyncRead
завершается, и обработчик как локальная переменная уничтожается, но замыкание, которое выполняется в io_service::run
, по-прежнему ссылается на эту переменную - поведение undefined.
Вам нужно захватить обработчик по значению, переместив его в поток и лямбда:
std::thread thread([ios,handler = std::move(handler)]() {
std::cout << std::time(nullptr) << ":run in thread before sleep"<< std::endl;
sleep(5);
std::cout << std::time(nullptr) << ":run in thread after sleep"<< std::endl;
std::cout << std::time(nullptr) << ":run in thread before cb" << std::endl;
ios->post([handler = std::move(handler)]() mutable {
handler(2);
});
std::cout << std::time(nullptr) << ":run in thread after cb"<<std::endl;
});
Этот Handler handler(std::forward<CompletionToken>(token));
создает handler
как локальную переменную внутри AsyncRead
функции. Когда выполняется следующее закрытие [&handler](){ handler(2); });
, оно вызывает handler(2)
, затем result.get()
возвращает 2, AsyncRead
функции заканчиваются, а handler
как локальная переменная AsyncRead
уничтожается. Но вы все еще ссылаетесь внутри замыкания на эту переменную, потому что вы захватываете ее по ссылке - это оборванная ссылка. handler
необходимо переместить или скопировать в закрытие, чтобы продлить срок его службы.
Добавляю сон после result.get()
, тоже краш. int val = result.get(); std::cout << std::time(nullptr) << ":after get == = " << std::endl; boost::asio::deadline_timer t(*ios, boost::posix_time::seconds(2)); t.async_wait(std::forward<CompletionToken>(token)); return val;
Я добавляю сон после result.get()
, AsyncRead завершится через 2 секунды, поэтому поток может завершиться первым, но также и сбой.
@ZhigangZhang Пожалуйста, не могли бы вы принять мой ответ? , тогда я смогу удалить его, потому что я хотел бы это сделать. Есть много вещей с boost::asio::spawn
, например, здесь играют роль сопрограммы. Вы можете запустить свое приложение под отладчиком, и вы увидите, что произойдет. Это не так просто, как вы думаете. Также нужна ссылка boost asio. Я ответил вам, что знаю, но вижу, что этого недостаточно.
Сбой происходит на *ec_=..
в обработчике класса, когда ec_ равно 0. И я использую watch point
, чтобы найти, кто изменил значение: [New Thread 0x7fa915f4a700 (LWP 7970)] Thread 1 "test1" hit Hardware watchpoint 3: *0x1ed82e8 Old value = 32342816 New value = 0 0x00007fa916c69477 in _Unwind_Resume () from /lib/x86_64-linux-gnu/libgcc_s.so.1
Что такое _Unwind_Resume? Почему он меняет значение?
`расслабиться` называется if ( from->do_unwind) throw forced_unwind();
в push_coroutine_impl::push
'результат.получить();' даст AsyncRead, почему локальная переменная «обработчик» уничтожена?