boost 协程在 async_write 操作后不会恢复

问题描述 投票:0回答:1

我有一个简单的 boost.coroutine,偶尔会被挂起?我不知道协程会发生什么。这很难重现,但确实发生了。 现在,从这个answer(point.2)我知道协程可能会达到糟糕的状态?那么如何知道呢? (这样我就可以生成一个新的)

示例伪代码-

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>

boost::asio::io_service io_service;
socket* socket_;


void write_to_socket(boost::asio::yield_context yield_context)
{

  while(true){
      // async write to socket
      err_code_ ec;
      socket_.async_write_some(boost::asio::buffer(buffer), yc[ec]);
      std::cout << "Done, now sleep for 10 ms" << std::endl;    
      timer.expires_from_now(boost::chrono::milliseconds(10));
      timer.async_wait(yc[ec]);
  }
}

int main ()
{
  boost::asio::spawn(io_service, &write_to_socket);
  while(true){
    io_service.run();
  }
}

输出如下所示...

Done, now sleep for 10 ms
Done, now sleep for 10 ms
.
.
.
Done, now sleep for 10 ms
Done, now sleep for 10 ms
<the program is just stuck here, nothing is printed after this>

尝试重现此问题,但无法重现,因为它是随机发生的。 如何知道协程是否已被破坏以便我可以再次生成它?谢谢。

c++11 boost boost-asio boost-coroutine
1个回答
0
投票

您假设了一些神奇的事实(“协程已被破坏”,“协程达到了糟糕的状态”)。这里没有魔法(除非你的程序有未定义的行为)。

另一个答案说的是,如果协程被挂起而无法恢复(意味着它的引用计数变为零,大概是当它返回或因异常退出时),它将被清理:这是您的常规引用计数资源管理。它不会“自发”发生。

您的示例缺少的是错误处理。事实上,补充一点:

void write_to_socket(asio::yield_context yc) {
    err_code_ ec;
    while (!ec.failed()) {
        // async write to socket
        socket_.async_write_some(asio::buffer(buffer), yc[ec]);
        std::cout << "Done, now sleep for 10 ms (" << ec.message() << ")" << std::endl;
        timer.expires_from_now(10ms);
        timer.async_wait(yc[ec]);
    }
    std::cout << "Coro done" << std::endl;
}

int main() {
    socket_.connect({{}, 8989});
    spawn(io_service, &write_to_socket);
    io_service.run();
    std::cout << "Bye" << std::endl;
}

当连接断开时,你可以看到它“失败”:

为什么你看不到

Coro done
Bye
?那是因为你在计时器等待中覆盖了
ec
。修复它:

    if (!ec) {
        timer.expires_from_now(10ms);
        timer.async_wait(yc[ec]);
    }

现在它会如你所期望的那样:

始终简化

在您的情况下,整个问题似乎是手动处理错误代码的选择(然后您完全忘记了)。为什么不使用异常?

void write_to_socket(asio::yield_context yc) try {
    while (true) {
        socket_.async_write_some(asio::buffer(buffer), yc);
        std::cout << "Done, now sleep for 10 ms" << std::endl;
        timer.expires_from_now(10ms);
        timer.async_wait(yc);
    }
} catch (boost::system::system_error const& se) {
    std::cout << "Exit: " << se.code().message() << std::endl;
}

这是我使用的完整测试程序:

住在Coliru

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using asio::ip::tcp;

asio::io_service   io_service;
tcp::socket        socket_{io_service};
asio::steady_timer timer{io_service};
std::string const  buffer = "SOMECONTENTNOTHINGTOSEE";

void write_to_socket(asio::yield_context yc) try {
    while (true) {
        socket_.async_write_some(asio::buffer(buffer), yc);
        std::cout << "Done, now sleep for 10 ms" << std::endl;
        timer.expires_from_now(10ms);
        timer.async_wait(yc);
    }
} catch (boost::system::system_error const& se) {
    std::cout << "Exit: " << se.code().message() << std::endl;
}

int main() {
    socket_.connect({{}, 8989});
    spawn(io_service, &write_to_socket);
    io_service.run();
    std::cout << "Bye" << std::endl;
}

测试

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_{coroutine,context,thread}
nc -lp 8989 -w 3 > /dev/null&
sleep 1; ./a.out&
sleep 3; kill %1

打印

Done, now sleep for 10 ms
Done, now sleep for 10 ms
Done, now sleep for 10 ms
Done, now sleep for 10 ms
Done, now sleep for 10 ms
Done, now sleep for 10 ms
...
Done, now sleep for 10 ms
Done, now sleep for 10 ms
bash: line 11: 24711 Terminated              nc -lp 8989 -w 3 > /dev/null
Done, now sleep for 10 ms
Exit: Broken pipe
Bye
© www.soinside.com 2019 - 2024. All rights reserved.