C++ 使用挂起的协同程序进行适当的清理
我想知道在这种情况下,什么是最好的(最干净,最难搞糟的)清理方法C++ 使用挂起的协同程序进行适当的清理,c++,boost,asynchronous,boost-asio,C++,Boost,Asynchronous,Boost Asio,我想知道在这种情况下,什么是最好的(最干净,最难搞糟的)清理方法 void MyClass::do_stuff(boost::asio::yield_context context) { while (running_) { uint32_t data = async_buffer->Read(context); // do other stuff } } Read是一个异步等待直到有数据要读取,然后返回该数据的调用。如果我想删除MyClass的这个实例,我如何确
void MyClass::do_stuff(boost::asio::yield_context context) {
while (running_) {
uint32_t data = async_buffer->Read(context);
// do other stuff
}
}
Read是一个异步等待直到有数据要读取,然后返回该数据的调用。如果我想删除MyClass的这个实例,我如何确保我这样做是正确的?假设这里的异步等待是通过截止时间计时器的异步等待执行的。如果我取消事件,我仍然必须等待线程完成“其他东西”的执行,然后才能知道事情处于良好状态(我无法加入线程,因为它属于io服务的线程,可能也在处理其他作业)。我可以这样做:
MyClass::~MyClass() {
running_ = false;
read_event->CancelEvent(); // some way to cancel the deadline_timer the Read is waiting on
boost::mutex::scoped_lock lock(finished_mutex_);
if (!finished_) {
cond_.wait(lock);
}
// any other cleanup
}
void MyClass::do_stuff(boost::asio::yield_context context) {
while (running_) {
uint32_t data = async_buffer->Read(context);
// do other stuff
}
boost::mutex::scoped_lock lock(finished_mutex_);
finished_ = true;
cond.notify();
}
但我希望使这些堆积如山的协同程序尽可能易于使用,而且人们不容易认识到这种情况的存在以及需要采取什么措施来确保事情得到妥善清理。有更好的办法吗?我在这里试图做的是在更基本的层面上错误的吗
此外,对于事件(我的答案与Tanner的答案基本相同),我需要以一种我必须保持一些额外状态的方式取消它(真正的取消与触发事件的正常取消相比)——如果有多个逻辑块等待同一事件,这将是不合适的。我很想知道是否有更好的方法来对异步事件建模,以便与协同程序挂起/恢复一起使用
谢谢
编辑:谢谢@Sehe,尝试了一个工作示例,我认为这说明了我的意思:
class AsyncBuffer {
public:
AsyncBuffer(boost::asio::io_service& io_service) :
write_event_(io_service) {
write_event_.expires_at(boost::posix_time::pos_infin);
}
void Write(uint32_t data) {
buffer_.push_back(data);
write_event_.cancel();
}
uint32_t Read(boost::asio::yield_context context) {
if (buffer_.empty()) {
write_event_.async_wait(context);
}
uint32_t data = buffer_.front();
buffer_.pop_front();
return data;
}
protected:
boost::asio::deadline_timer write_event_;
std::list<uint32_t> buffer_;
};
class MyClass {
public:
MyClass(boost::asio::io_service& io_service) :
running_(false), io_service_(io_service), buffer_(io_service) {
}
void Run(boost::asio::yield_context context) {
while (running_) {
boost::system::error_code ec;
uint32_t data = buffer_.Read(context[ec]);
// do something with data
}
}
void Write(uint32_t data) {
buffer_.Write(data);
}
void Start() {
running_ = true;
boost::asio::spawn(io_service_, boost::bind(&MyClass::Run, this, _1));
}
protected:
boost::atomic_bool running_;
boost::asio::io_service& io_service_;
AsyncBuffer buffer_;
};
类异步缓冲区{
公众:
异步缓冲区(boost::asio::io_服务和io_服务):
写入事件(io服务){
写入事件过期时间(boost::posix\u time::pos\u infin);
}
无效写入(uint32_t数据){
缓冲区向后推(数据);
写入事件取消();
}
uint32\u t读取(boost::asio::yield\u上下文){
if(缓冲区为空(){
写入事件异步等待(上下文);
}
uint32_t data=buffer_u.front();
缓冲区(前);
返回数据;
}
受保护的:
boost::asio::截止时间\计时器写入\事件\uU;
列表缓冲区;
};
类MyClass{
公众:
MyClass(boost::asio::io_服务和io_服务):
正在运行(false)、io_服务(io_服务)、缓冲区(io_服务){
}
无效运行(boost::asio::yield\u上下文){
当(运行时){
boost::system::error_code ec;
uint32_t data=缓冲区读取(上下文[ec]);
//处理数据
}
}
无效写入(uint32_t数据){
缓冲区写入(数据);
}
void Start(){
运行=真;
boost::asio::spawn(io_服务,boost::bind(&MyClass::Run,this,_1));
}
受保护的:
boost::原子布尔运行;
boost::asio::io_服务和io_服务;
异步缓冲区;
};
在这里,假设缓冲区是空的,MyClass::Run当前在调用Read时挂起,因此有一个截止时间\u timer.async\u wait,等待事件触发以恢复该上下文。是时候销毁MyClass的这个实例了,那么我们如何确保它干净地完成呢。更典型的方法是使用
boost::使用MyClass
从这个
启用共享,并将这些方法绑定到共享指针上运行
boostbind支持透明地绑定到Boost::shared_ptr
这样,您可以仅在最后一个用户消失时自动运行析构函数
如果你创建了一个SSCCE,我很乐意改变它,来展示我的意思
更新 致SSCCEE:一些评论:
- 我想象了一个运行IO服务的线程池
直接调用MyClass
成员函数的方式不是线程安全的。实际上,没有线程安全的方法来取消生产者线程之外的事件,因为生产者已经访问缓冲区进行写入。这可以通过使用线程来缓解(在当前的设置中,我不知道MyClass是如何实现线程安全的)。或者,查看活动对象模式(对于该模式,Tanner有一个很好的答案[2],依此类推) 为了简单起见,我选择了strand方法,因此我们:AsyncBuffer
void MyClass::Write(uint32_t data) { strand_.post(boost::bind(&AsyncBuffer::Write, &buffer_, data)); }
- 你问
此外,对于事件(我的答案基本上与Tanner的答案相同),我需要以一种我必须保持一些额外状态的方式取消它(真正的取消与用于触发事件的正常取消)
这种状态最自然的地方是截止时间计时器:这是截止时间。通过重置计时器停止缓冲区:
这会立即取消计时器,但可以检测到,因为最后期限已过void AsyncBuffer::Stop() { // not threadsafe! write_event_.expires_from_now(boost::posix_time::seconds(-1)); }
MyClass::Run
coroutine的“sniper线程”。主线是狙击线
查看它
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
#include <list>
#include <iostream>
// for refcounting:
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
namespace asio = boost::asio;
class AsyncBuffer {
friend class MyClass;
protected:
AsyncBuffer(boost::asio::io_service &io_service) : write_event_(io_service) {
write_event_.expires_at(boost::posix_time::pos_infin);
}
void Write(uint32_t data) {
buffer_.push_back(data);
write_event_.cancel();
}
uint32_t Read(boost::asio::yield_context context) {
if (buffer_.empty()) {
boost::system::error_code ec;
write_event_.async_wait(context[ec]);
if (ec != boost::asio::error::operation_aborted || write_event_.expires_from_now().is_negative())
{
if (context.ec_)
*context.ec_ = boost::asio::error::operation_aborted;
return 0;
}
}
uint32_t data = buffer_.front();
buffer_.pop_front();
return data;
}
void Stop() {
write_event_.expires_from_now(boost::posix_time::seconds(-1));
}
private:
boost::asio::deadline_timer write_event_;
std::list<uint32_t> buffer_;
};
class MyClass : public boost::enable_shared_from_this<MyClass> {
boost::atomic_bool stopped_;
public:
MyClass(boost::asio::io_service &io_service) : stopped_(false), buffer_(io_service), strand_(io_service) {}
void Run(boost::asio::yield_context context) {
while (!stopped_) {
boost::system::error_code ec;
uint32_t data = buffer_.Read(context[ec]);
if (ec == boost::asio::error::operation_aborted)
break;
// do something with data
std::cout << data << " " << std::flush;
}
std::cout << "EOF\n";
}
bool Write(uint32_t data) {
if (!stopped_) {
strand_.post(boost::bind(&AsyncBuffer::Write, &buffer_, data));
}
return !stopped_;
}
void Start() {
if (!stopped_) {
stopped_ = false;
boost::asio::spawn(strand_, boost::bind(&MyClass::Run, shared_from_this(), _1));
}
}
void Stop() {
stopped_ = true;
strand_.post(boost::bind(&AsyncBuffer::Stop, &buffer_));
}
~MyClass() {
std::cout << "MyClass destructed because no coroutines hold a reference to it anymore\n";
}
protected:
AsyncBuffer buffer_;
boost::asio::strand strand_;
};
int main()
{
boost::thread_group tg;
asio::io_service svc;
{
// Start the consumer:
auto instance = boost::make_shared<MyClass>(svc);
instance->Start();
// Sniper in 2 seconds :)
boost::thread([instance]{
boost::this_thread::sleep_for(boost::chrono::seconds(2));
instance->Stop();
}).detach();
// Start the producer:
auto producer_coro = [instance, &svc](asio::yield_context c) { // a bound function/function object in C++03
asio::deadline_timer tim(svc);
while (instance->Write(rand())) {
tim.expires_from_now(boost::posix_time::milliseconds(200));
tim.async_wait(c);
}
};
asio::spawn(svc, producer_coro);
// Start the service threads:
for(size_t i=0; i < boost::thread::hardware_concurrency(); ++i)
tg.create_thread(boost::bind(&asio::io_service::run, &svc));
}
// now `instance` is out of scope, it will selfdestruct after the snipe
// completed
boost::this_thread::sleep_for(boost::chrono::seconds(3)); // wait longer than the snipe
std::cout << "This is the main thread _after_ MyClass self-destructed correctly\n";
// cleanup service threads
tg.join_all();
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
//对于重新计数:
#包括
#包括
名称空间asio=boost::asio;
类异步缓冲区{
朋友班我班;
受保护的:
AsyncBuffer(boost::asio::io_服务和io_服务):写入事件(io_服务){
写入事件过期时间(boost::posix\u time::pos\u infin);
}
无效写入(uint32_t数据){
缓冲区向后推(数据);
写入事件取消();
}
uint32\u t读取(boost::asio::yield\u上下文){
if(缓冲区为空(){
boost::system::error_code ec;
写入事件异步等待(上下文[ec]);
如果(ec!=boost::asio::error::operation_中止| | | write_事件u.expires u from_now().为负()
{
if(context.ec_389;)
*context.ec=boost::asio::error::operation\u中止;
返回0;
}