使用boost asio捕获Ctrl-C信号

4

我正在尝试在应用程序中捕获Ctrl-C,如下所示的MWE示例:

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

void handler( const boost::system::error_code& error , int signal_number )
{
    std::cout << "handling signal " << signal_number << std::endl;
}

int main( int argc , char** argv )
{
    boost::asio::io_service io_service;

    // Construct a signal set registered for process termination.
    boost::asio::signal_set signals(io_service, SIGINT );

    // Start an asynchronous wait for one of the signals to occur.
    signals.async_wait( handler );

    char choice;
    while( true )
    {
        std::cout << "Press a key: " << std::endl;
        std::cin >> choice; 
    }
}

很遗憾,当我按下Ctrl+C时,handler()没有被调用。相反地,循环不再等待用户输入,如下所示:

c:\tmp>CtrlC.exe
Press a key:
d
Press a key:
e
Press a key:
Press a key:
Press a key:
Press a key:
Press a key:
Press a key:
Press a key:
Press a key:
Press a key:

如果有影响的话,我正在使用Windows操作系统,我知道在Windows上有一种特定的方式来捕获Ctrl+C,但如果可能的话,我想使用Boost库以便于移植。

你的 std::cin >> 是一个阻塞操作。此时 io_service 不起作用。 - Galimov Albert
2个回答

7

Boost ASIO 的核心是 io_service 对象,所有你请求的任务(特别是异步任务)通常都由 io_service 处理。

通过这样做:

// Start an asynchronous wait for one of the signals to occur.
signals.async_wait( handler );

您需要异步等待信号并在发生时调用您的handler来使用该服务。

问题是,您的io_service未运行。

因此,如果您希望它正常工作,您需要启动它:

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

void handler( const boost::system::error_code& error , int signal_number )
{
  std::cout << "handling signal " << signal_number << std::endl;
  exit(1);
}

int main( int argc , char** argv )
{
  boost::asio::io_service io_service;

  // Construct a signal set registered for process termination.
  boost::asio::signal_set signals(io_service, SIGINT );

  // Start an asynchronous wait for one of the signals to occur.
  signals.async_wait( handler );

  io_service.run();
}

然后按下Ctrl+C将会按照您的预期执行。

请注意,io_service基本上取代了您的无限循环,因此您在其中所做的任何事情都应成为io_service的任务。


我已经将无限循环封装在一个名为func的函数中,并将其传递给了io_service,代码如下:io_service.post( func ),但是我得到的结果仍然相同。 - Olumide
1
你的无限循环就是io_service,不要再嵌套另一个无限循环,只需放置你想要执行的操作,如果需要重复执行,请在完成后重新提交。 - Drax
如何进行转发?如果在func()中这样做,我会得到一个无限循环。 - Olumide
这对他不起作用,因为他还需要轮询用户输入。@Olumide 您应该在不同的线程中运行 io_service.run(),并在主线程中运行您的 while 循环。 - Arunmu
@Arunmu 我希望能够在不(明确)编写多线程代码的情况下完成这个任务。 - Olumide
显示剩余4条评论

2

你需要让io_service执行已传递给它的处理程序。只有在调用runrun_one方法时,io_service才能这样做。你可以在while循环中使用run_one方法:

#include <boost/asio/signal_set.hpp>
#include <iostream>
#include <cstdlib>
#include <limits>

void handler( const boost::system::error_code& error , int signal_number )
{
    // Not safe to use stream here...
    std::cout << "handling signal " << signal_number << std::endl;
    exit (1);
}

int main( int argc , char** argv )
{
    boost::asio::io_service io_service;

    // Construct a signal set registered for process termination.
    boost::asio::signal_set signals(io_service, SIGINT );

    // Start an asynchronous wait for one of the signals to occur.
    signals.async_wait( handler );

    char choice;
    while( true )
    {
        std::cout << "Press a key: " << std::endl;
        std::cin >> choice;
        io_service.run_one();
    }
}

在这种特殊情况下,在处理程序中使用cout是安全的吗?只有一个执行线程。 - Torkel Bjørnson-Langen
不,你永远不知道信号处理程序在什么时候被调用。如果在处理程序被调用之前,cout流缓冲区中已经有了一些部分写入的数据,那该怎么办呢?在我的情况下,影响可能较小,但最好避免在信号处理程序中访问任何共享资源。 - Arunmu
1
你是在谈论“POSIX风格的信号”吗?我很确定Boost.Asio不会从(POSIX)信号处理程序调用传递给signals.async_wait的处理程序。因此,我不明白在“部分数据写入cout流缓冲区”时如何调用处理程序。但我同意最好避免这种情况。 - Torkel Bjørnson-Langen
啊..是的。我的思维完全被POSIX所占据了..抱歉。那么你可能是正确的。 - Arunmu
-1 抱歉,这并不是问题作者应该采取的好方法。std::cin >> choice仍然是一个阻塞式系统调用!应该通过boost::asio来读取输入,而不是直接从std::cin中读取。因此,最终应用程序将等待两个事件:输入字符可用或信号被传递。 - Kai Petzke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接