为什么提振。Asio不支持基于事件的接口吗?

时间:2022-06-19 00:26:55

I am attempting to understand Boost.Asio, with the intention of potentially implementing a signaling system using condition variables in conjunction with Boost.Asio.

我正在尝试理解Boost。Asio与Boost.Asio结合使用条件变量可能实现一个信号系统。

I have seen the other * questions boost asio asynchronously waiting on a condition variable, boost::asio async condition, and boost condition variable issue, but none of these questions/answers have satisfactorily touched on an essential question that I have: Is it true that, and/or is there a fundamental reason why, Boost.Asio is not applicable to, or a natural fit with, condition variables?

我见过其他*问题在条件变量boost asio异步等待时,boost:::asio异步条件,和boost条件变量问题,但是这些问题/答案都没有令人满意地触及我的一个基本问题:是真的吗?Asio不适用于条件变量,还是与条件变量自然匹配?

My thinking is that condition variables are internally implemented using operating-system level synchronization objects (for example, boost::thread::condition_variable on Windows uses a Windows OS semaphore). Because, to my current understanding, boost::asio::io_service is intended to encapsulate OS-level synchronization objects, condition variables would therefore seem to be a natural fit.

我的想法是,条件变量在内部使用操作系统级同步对象实现(例如,boost:::thread: condition_variable on Windows使用Windows OS信号量)。根据我目前的理解,boost: asio: io_service旨在封装os级别的同步对象,因此条件变量似乎是一种自然的匹配。

It is true that unlike file operations and socket operations, there is typically never a callback function at the operating system level associated with a signaled condition (I think - I am not sure about this). However, it would seem simple enough to implement such a callback handler within Boost.Asio by simply requiring the user to provide a callback function that is to be called when a condition variable is signaled - just as users must provide a completion handler routine for other boost::asio::io_service services.

的确,与文件操作和套接字操作不同,在操作系统级别上通常不存在与有信号条件相关联的回调函数(我认为——我对此不确定)。但是,在Boost中实现这样一个回调处理程序似乎足够简单。Asio只需要求用户提供回调函数,当条件变量发出信号时就会调用这个函数——就像用户必须为其他boost: Asio:io_service服务提供一个完成处理程序例程一样。

For example (this is just a quick thought, not a complete prototype - it does not include sufficient parameters to deal with notify_one() vs. notify_all(), doesn't indicate how the service knows when to exit, and likely has other glaring omissions or flaws):

例如(这只是一个快速的想法,而不是一个完整的原型—它没有包含足够的参数来处理notify_one()和notify_all(),它没有指示服务如何知道何时退出,并且很可能还有其他明显的遗漏或缺陷):

void condition_handler_function() {}
boost::asio::io_service service;
boost::mutex mut;
boost::condition_variable cond;

// The following class is **made up by me** - would such a class be a good idea?
boost::asio::io_service::condition_service
             condserv(service, cond, mut, condition_handler_function); 

condserv.async_wait_on_signal();

service.run(); // when condition variable is signaled by notify_one(),
               // 'handler_function()' would be called


// ... in some other thread, later:
cond.notify_one(); // This would trigger 'handler_function()'
                   // in this theoretical code

Perhaps, if I tried to fill in the missing details noted above the code snippet, it would become clear to me that this could not work in a clean way. However, this effort is non-trivial.

也许,如果我试图填写上面代码片段中所提到的缺失的细节,我就会明白这不能以一种干净的方式工作。然而,这种努力并非微不足道。

Therefore, I would like to post the question here. Is there a good reason why condition variables are not supported by Boost.Asio?

因此,我想在这里提出这个问题。为什么Boost.Asio不支持条件变量?

ADDENDUM

齿顶高

I have changed the title of the post to reference "Event-based interface", since Tanner's answer, below, has clarified to me that it is really an Event-based interface that I am asking about (not really condition variables).

我已经将文章的标题改为引用“基于事件的接口”,因为Tanner下面的回答已经向我阐明了我要问的实际上是一个基于事件的接口(不是真正的条件变量)。

2 个解决方案

#1


14  

Boost.Asio is a C++ library for network and low-level I/O programming. As such, OS-level synchronization objects, such as condition variables, are outside of the scope of the library, and a much better fit for Boost.Thread. The Boost.Asio author often presents the boost::asio::io_service as the bridge or link between the application and the OS. While this may be an over simplification, it is within the context of the OS's I/O services.

提振。Asio是一个用于网络和低级I/O编程的c++库。因此,操作系统级同步对象(如条件变量)不在库的范围之内,更适合于boot . thread。的提振。Asio的作者经常提出boost:: Asio:io_service作为应用程序和操作系统之间的桥梁或链接。虽然这可能过于简化,但它是在操作系统的I/O服务上下文中进行的。

Asynchronous programming already has an innate complexity due to the separation in time and space between operation initiation and completion. Strands provided a fairly clean solution to provide strict sequential invocation of handlers, without the need of explicit locking. As the locking is both implicit and thread-safe, application code can use strands without the fear of deadlocking. On the other hand, having boost::asio::io_service::condition_service perform implicit synchronization on an externally provided object may turn a complex library into a complicated one. It may not be clear to the application developer what mutex on which the handler was synchronized, and the state of the mutex. Additionally, it introduces the ability for applications to more easily deadlock the event processing loop due to the implicit locking.

由于操作起始和完成之间在时间和空间上的分离,异步编程已经具有固有的复杂性。strand提供了一个相当干净的解决方案,可以提供严格的顺序处理程序调用,而不需要显式的锁定。由于锁定是隐式的和线程安全的,所以应用程序代码可以使用链而不用担心死锁。另一方面,有boost::asio::io_service::condition_service在外部提供的对象上执行隐式同步,可能将复杂的库变成复杂的库。应用程序开发人员可能不清楚处理程序同步的互斥对象是什么,以及互斥对象的状态。此外,它还引入了应用程序能够更容易地由于隐式锁定而死锁事件处理循环的功能。


If event-based handler invocation needs to occur, then one fairly simple alternative is use the same approach Boost.Asio's timeout server example uses: boost::asio::deadline_timer. A deadline_timer's expiry time can be set to posix_time::pos_infin, causing an async_wait's handler to only be invoked once the timer has been canceled:

如果需要发生基于事件的处理程序调用,那么一个相当简单的替代方法就是使用相同的方法Boost。Asio的超时服务器示例使用:boost::: Asio::deadline_timer。一个死线_timer的过期时间可以被设置为posix_time::pos_infin,导致只有在取消计时器之后才调用async_wait的处理程序:

  • cancel() could function as notify_all(), where all outstanding handlers are queued for invocation.
  • cancel()可以作为notify_all()函数,其中所有未完成的处理程序都排队等待调用。
  • cancel_one() could function as notify_one(), where a max of one outstanding handler is queued for invocation.
  • cancel_one()可以作为notify_one()函数,在这里,一个未处理的处理器的最大值被排队调用。

A simple example, ignoring error code handling, is as follows:

忽略错误代码处理的一个简单示例如下:

#include <iostream>

#include <boost/asio.hpp>
#include <boost/thread.hpp>

class event
{
public:
  explicit
  event(boost::asio::io_service& io_service) 
    : timer_(io_service)
  {
    // Setting expiration to infinity will cause handlers to
    // wait on the timer until cancelled.
    timer_.expires_at(boost::posix_time::pos_infin);
  }

  template <typename WaitHandler>
  void async_wait(WaitHandler handler)
  {
    // bind is used to adapt the user provided handler to the deadline 
    // timer's wait handler type requirement.
    timer_.async_wait(boost::bind(handler));
  }

  void notify_one() { timer_.cancel_one(); }
  void notify_all() { timer_.cancel();     }

private:
  boost::asio::deadline_timer timer_;
};

void on_event() { std::cout << "on_event" << std::endl; }

int main()
{
  boost::asio::io_service io_service;
  event event(io_service);

  // Add work to service.
  event.async_wait(&on_event);

  // Run io_service.
  boost::thread thread(boost::bind(&boost::asio::io_service::run,
                       &io_service));

  // Trigger event, causing the on_event handler to run.
  event.notify_one();

  thread.join();  
}

#2


3  

Condition variables are a synchronous concept: they block one thread until something happens in another thread. Boost.Asio is an asynchronous framework: it provides multiplexing of events in a non-blocking fashion. These two don't seem very compatible. If you want asynchronous event notification, look at eventfd on Linux, which should be usable with Boost.Asio.

条件变量是一个同步的概念:它们阻塞一个线程,直到另一个线程中发生什么事情。提振。Asio是一个异步框架:它以非阻塞的方式提供事件的多路复用。这两个似乎不太相容。如果您想要异步事件通知,请查看Linux上的eventfd,它应该可以使用Boost.Asio。

#1


14  

Boost.Asio is a C++ library for network and low-level I/O programming. As such, OS-level synchronization objects, such as condition variables, are outside of the scope of the library, and a much better fit for Boost.Thread. The Boost.Asio author often presents the boost::asio::io_service as the bridge or link between the application and the OS. While this may be an over simplification, it is within the context of the OS's I/O services.

提振。Asio是一个用于网络和低级I/O编程的c++库。因此,操作系统级同步对象(如条件变量)不在库的范围之内,更适合于boot . thread。的提振。Asio的作者经常提出boost:: Asio:io_service作为应用程序和操作系统之间的桥梁或链接。虽然这可能过于简化,但它是在操作系统的I/O服务上下文中进行的。

Asynchronous programming already has an innate complexity due to the separation in time and space between operation initiation and completion. Strands provided a fairly clean solution to provide strict sequential invocation of handlers, without the need of explicit locking. As the locking is both implicit and thread-safe, application code can use strands without the fear of deadlocking. On the other hand, having boost::asio::io_service::condition_service perform implicit synchronization on an externally provided object may turn a complex library into a complicated one. It may not be clear to the application developer what mutex on which the handler was synchronized, and the state of the mutex. Additionally, it introduces the ability for applications to more easily deadlock the event processing loop due to the implicit locking.

由于操作起始和完成之间在时间和空间上的分离,异步编程已经具有固有的复杂性。strand提供了一个相当干净的解决方案,可以提供严格的顺序处理程序调用,而不需要显式的锁定。由于锁定是隐式的和线程安全的,所以应用程序代码可以使用链而不用担心死锁。另一方面,有boost::asio::io_service::condition_service在外部提供的对象上执行隐式同步,可能将复杂的库变成复杂的库。应用程序开发人员可能不清楚处理程序同步的互斥对象是什么,以及互斥对象的状态。此外,它还引入了应用程序能够更容易地由于隐式锁定而死锁事件处理循环的功能。


If event-based handler invocation needs to occur, then one fairly simple alternative is use the same approach Boost.Asio's timeout server example uses: boost::asio::deadline_timer. A deadline_timer's expiry time can be set to posix_time::pos_infin, causing an async_wait's handler to only be invoked once the timer has been canceled:

如果需要发生基于事件的处理程序调用,那么一个相当简单的替代方法就是使用相同的方法Boost。Asio的超时服务器示例使用:boost::: Asio::deadline_timer。一个死线_timer的过期时间可以被设置为posix_time::pos_infin,导致只有在取消计时器之后才调用async_wait的处理程序:

  • cancel() could function as notify_all(), where all outstanding handlers are queued for invocation.
  • cancel()可以作为notify_all()函数,其中所有未完成的处理程序都排队等待调用。
  • cancel_one() could function as notify_one(), where a max of one outstanding handler is queued for invocation.
  • cancel_one()可以作为notify_one()函数,在这里,一个未处理的处理器的最大值被排队调用。

A simple example, ignoring error code handling, is as follows:

忽略错误代码处理的一个简单示例如下:

#include <iostream>

#include <boost/asio.hpp>
#include <boost/thread.hpp>

class event
{
public:
  explicit
  event(boost::asio::io_service& io_service) 
    : timer_(io_service)
  {
    // Setting expiration to infinity will cause handlers to
    // wait on the timer until cancelled.
    timer_.expires_at(boost::posix_time::pos_infin);
  }

  template <typename WaitHandler>
  void async_wait(WaitHandler handler)
  {
    // bind is used to adapt the user provided handler to the deadline 
    // timer's wait handler type requirement.
    timer_.async_wait(boost::bind(handler));
  }

  void notify_one() { timer_.cancel_one(); }
  void notify_all() { timer_.cancel();     }

private:
  boost::asio::deadline_timer timer_;
};

void on_event() { std::cout << "on_event" << std::endl; }

int main()
{
  boost::asio::io_service io_service;
  event event(io_service);

  // Add work to service.
  event.async_wait(&on_event);

  // Run io_service.
  boost::thread thread(boost::bind(&boost::asio::io_service::run,
                       &io_service));

  // Trigger event, causing the on_event handler to run.
  event.notify_one();

  thread.join();  
}

#2


3  

Condition variables are a synchronous concept: they block one thread until something happens in another thread. Boost.Asio is an asynchronous framework: it provides multiplexing of events in a non-blocking fashion. These two don't seem very compatible. If you want asynchronous event notification, look at eventfd on Linux, which should be usable with Boost.Asio.

条件变量是一个同步的概念:它们阻塞一个线程,直到另一个线程中发生什么事情。提振。Asio是一个异步框架:它以非阻塞的方式提供事件的多路复用。这两个似乎不太相容。如果您想要异步事件通知,请查看Linux上的eventfd,它应该可以使用Boost.Asio。