【问题标题】:Safely interrupt C++11 blocking operation安全中断 C++11 阻塞操作
【发布时间】:2016-02-08 09:36:32
【问题描述】:

我有一个std::thread,它使用 Boost 的 asio 从串口读取数据:

std::atomic<bool> quit(false);

void serialThread()
{
    try
    {
        asio::io_service io;
        asio::serial_port port(io);

        port.open("COM9"); // Yeay no port enumeration support!

        port.set_option(asio::serial_port_base::baud_rate(9600));

        while (!quit)
        {
            asio::streambuf buf;
            asio::read_until(port, buf, "\n");

            auto it = asio::buffers_begin(buf.data());
            string line(it, it + buf.size());

            doStuffWithLine(line);
        }
    }
    catch (std::exception e)
    {
        cout << "Serial thread error: " << e.what() << endl;
    }
}

void SetupSignals()
{
    // Arrange it so that `quit = true;` happens when Ctrl-C is pressed.
}

int main(int argc, char *argv[])
{
    SetupSignals();

    thread st(serialThread);

    st.join();

    return 0;
}

当我按下 Ctrl-C 时,我想干净地 退出线程,以便正确调用所有析构函数(如果您不这样做,Windows 上的某些驱动程序会讨厌它正确关闭他们的资源)。

不幸的是,如您所见,read_until() 中的当前代码块,所以当您按下 Ctrl-C 时,在收到新的文本行之前什么都不会发生。

一种解决方案是使用轮询,如下所示:

asio::async_read_until(port, buf, "\n", ...);
while (!quit)
    io.poll();

但我宁愿不使用轮询。这很不雅。我目前能看到的唯一解决方案是在quit 设置为true 或读取完成时触发std::condition_variable quitOrIoFinished。但是我没有写 asio 所以我不能给它一个条件变量来等待。

有什么干净、理智的解决方案吗?在 Go 中,我只需使用 select 来等待多个频道,其中一个是退出频道。不过,我在 C++ 中看不到类似的解决方案。

【问题讨论】:

  • 很确定有一种方法可以指定asio::read_until 的超时时间。将该超时设置为 200 毫秒,并在重试之前检查您是否应该退出。
  • 是的,这只是不那么激进的轮询...我想要一个非轮询解决方案。

标签: c++ multithreading c++11 boost condition-variable


【解决方案1】:

使用asio::signal_set 等待 INT 信号(control-C 倾向于发送中断)。

当它到达时,只需在您的 IO 对象上调用 cancel() 并挂起异步操作。他们将返回 error_code 等于 boost::asio::error::operation_aborted

现在,如果您有一个io_service::work 对象,请将其销毁,所有运行io_service::run() 的线程都会返回,因此您可以加入它们。

注意注意同步访问您的 IO 对象(例如,当您在它们上调用 cancel() 时)因为这些对象不是线程安全的,不像 io_servicestrand

【讨论】:

  • 这个答案是不安全的:从信号处理程序调用 cancel() 或任何 ASIO 是未定义的行为。您只能调用信号处理程序安全函数,这是一个非常有限的子集,大多数人发出信号量或将字节写入管道以唤醒线程来处理信号。我建议不要改变那个成语。
  • @NiallDouglas 在我的回答中我没有建议处理原始信号处理程序。这是 ASIO 的详细信息
  • 是的,你是对的。对不起,在回复之前责备缺乏咖啡。我要提一下,尽管我不相信 signal_set 是可移植的,例如,Windows 不使用信号,要在 Ctrl-C 那里安装控制台控制处理程序。
猜你喜欢
  • 2017-02-26
  • 2010-12-21
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 2013-07-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多