取消或只是正确阻止套接字上的任何未来异步操作

时间:2017-12-01 15:14:42

标签: c++ multithreading sockets concurrency boost-asio

我在asio :: ip :: tcp :: socket上有一系列嵌套异步操作,我想有可能从其他线程尽快停止它。 这是一些伪:

socket->async_something(buffer, CallbackLambdaHeader
{
    if (errorcode)
    {
        //exit the sequence
    }
    // some code
    socket->async_something(buffer, CallbackLambdaHeader
    {
        if (errorcode)
        {
            //exit the sequence
        }
        // some code
        socket->async_something(buffer, CallbackLambdaHeader
        {
            if (errorcode)
            {
                //exit the sequence
            }
            // etc
        });
    });
});

问题是简单的socket->cancel()调用不会一直有效,因为在调用的那一刻,其中一个回调可能正在运行。因此,不会有取消排队的操作,但正在运行的回调可以很快添加一个并继续序列。 我希望socket->cancel()之后的异步操作也被取消(立即完成operation_aborted错误)。

唯一的想法是以某种方式将每个呼叫包裹在mutex.lock()中并使用取消标记

//thread1:
mutex.lock();
cancel = true;
socket->cancel();
mutex.unlock();

//thread 2:
mutex.lock();
if (!cancel)
{
    socket->async_something(buffer, CallbackLambdaHeader
    {
        if (errorcode)
        {
            //exit the sequence
        }
        // some code
        mutex.lock();
        if (!cancel)
        {
            socket->async_something(buffer, CallbackLambdaHeader
            {
                if (errorcode)
                {
                    //exit the sequence
                }
                // some code
                mutex.lock();
                if (!cancel)
                {
                    socket->async_something(buffer, CallbackLambdaHeader
                    {
                        if (errorcode)
                        {
                            //exit the sequence
                        }
                        // etc
                    });
                }
                else
                {
                    //exit the sequence
                }
                mutex.unlock();
            });
        }
        else
        {
            //exit the sequence
        }
        mutex.unlock();
    });
}
else
{
    //exit the sequence
}
mutex.unlock();

但它看起来很糟糕。
听说过io_service::strand,但不知道如何在这里使用它。使用.post()和socket->cancel()方法?有没有保证情况{回调,取消,回调}是不可能的?

1 个答案:

答案 0 :(得分:0)

{callback,cancel,callback}总是可行的,这不是你想要避免的。事实上,任何取消将始终导致任何待处理操作完成,因此至少会有一个回调(带有错误代码表明操作已中止)。

一旦您知道套接字已被“取消”(取消操作不会这样做:它取消操作,您希望/需要避免的是安排新任务 >,而不是套接字)。

我看到两件事情,视情况而定,可能是你想要的:

  1. 如果要关闭套接字及其上的所有操作,请永久

    我只是发布一个任务来关闭套接字(当然也会取消挂起的操作)。好处是你可以在意外地为该套接字安排新操作之前检查套接字状态[sock.is_open] http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/reference/basic_stream_socket/is_open.html)。

    _service.post([this] { sock.close(); });
    
      

    如果您有多个线程sock - ning服务,您需要一个链来避免数据争用访问run变量。见Why do I need strand per connection when using boost::asio?

  2. 如果要取消特定操作链而使套接字保持打开¹,则每个套接字都可以有一个标志,指示操作何时被取消。

    所涉及的所有完成处理程序已经收到包含error_code的{​​{1}},因此应该很容易避免在该链中进行“跟进”工作。

    在回调lambda标题中输入这样的内容:

    boost::asio::error::operation_aborted

    事实上,您可能希望在大多数错误上执行此操作。

        if (ec == boost::asio::error::operation_aborted)
             return;
    

    如果某些其他线程即将发布新的独立异步操作(链)并且您想要阻止它(仅在没有关闭的情况下),该标志仅仅是有利的套接字,否则使用解决方案#1)。

    该套接字不需要同步,因为您不与尚未共享 if (ec) return; // log `ec.message()`? 的任何人共享该套接字。 <{1}}也不是线程安全的,因此您知道访问该标志必须是安全的。

      

    同样,如果那不是真的,你需要一条链开始。

    &GT;

      

    拥有一个服务线程意味着你有一个隐含的链,你可以避免复杂性,即使你可能有许多线程通过永远不直接发布异步操作来发布任务:

    sock
         

    这样可以确保所有套接字操作都在隐式链上。

  3. ¹我实际上想不出一个有意义的协议,但也许是数据报协议(?)