asio乱写——io_service之post方法

本文深入探讨了Boost.Asio库中的io_service类及其关键方法post的实现原理,包括使用handle_wrapper对象包装并调度函数的过程,以及如何通过do_one方法执行事件监控和回调。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <string>

using namespace boost::asio;

void PrintSomething(const std::string& msg) {
    std::cout << msg << '\n';
}

void PrintHello() {
    std::cout << "Hello\n";
}

int main() {
    io_service service;

    service.post(PrintHello);
    service.post(boost::bind(PrintSomething, "whoami"));

    //service.run_one();
    service.run();

    return 0;
}


PrintHello函数的类型为void (*)();

template <typename Handler>
void post(Handler handler)
{
  // Allocate and construct an operation to wrap the handler.
  // 生成handle_wrapper<void (*)()>对象,返回指向handle_wrapper<void (*)()>对象的指针
  // 并将对象的所有权转移给scoped_ptr智能指针对象
  // 该智能指针对象如果在生命周期内没有将对象的所有权转移则会在析构时删除其所指向的对象
  handler_queue::scoped_ptr ptr(handler_queue::wrap(handler));

  boost::asio::detail::mutex::scoped_lock lock(mutex_);

  // If the service has been shut down we silently discard the handler.
  if (shutdown_)
    return;

  // Add the handler to the end of the queue.
  // handle_queue_与ptr对象同时持有指向handle_wrapper<void (*)()>对象的指针
  handler_queue_.push(ptr.get());
  // 将scoped_ptr对象对指针的所有权转移给handler_queue_,保证其析构时不会释放原来其指向的对象
  ptr.release();

  // An undelivered handler is treated as unfinished work.
  ++outstanding_work_;

  // Wake up a thread to execute the handler.
  if (!interrupt_one_idle_thread(lock))
  {
    if (!task_interrupted_ && task_)
    {
      task_interrupted_ = true;
      task_->interrupt();
    }
  }
}

post方法中调用handle_queue::wrap(handler)时,编译器会根据模板方法生成新的实例方法:

template<>
static handler* wrap(void (*)() h) {
	typedef handle_wrapper<void (*)()> value_type;
	typedef handle_alloc_traits<void (*)(), handle_wrapper<void (*)()> > alloc_traits;
	
	//下面的操作将new操作分成了两步:分配新建对象所需空间,构造对象并与空间关联(将对象分布到上一步所建的空间中);
	// 实际上C++中的new操作都是分成这两步。
	
	//为handle_wrapper<void (*)()>对象分配存储空间
	raw_handler_ptr<alloc_traits> raw_ptr(h);	
	//调用handle_wrapper<void (*)()>构造函数创建对象且存储到上一步分配的空间中,并将对象的所有权转移支handler_ptr	
	handler_ptr<alloc_traits> ptr(raw_ptr, h);	
	//返回对象指针并将对象的所有权转移给wrap调用者
	return ptr.release();												
  
	//如果不进行这一系列的所有权转移,新生成的对象将在raw_handler_ptr或handler_ptr对象析构时释放
}

handler_alloc_traits的定义:

template <typename Handler, typename Object>
struct handler_alloc_traits
{
  typedef Handler handler_type;		//函数指针对象类型
  typedef Object value_type;		//要创建的对象类型
  typedef Object* pointer_type;		//指向创建对象的指针类型
  BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object)); //需要创建对象的大小
};

task_io_service对象作为io_service平台相关的服务提供与平台相关的run,run_one,poll,poll_one方法,其中都调用了do_one这个方法:

// do_one方法所做的主要工作:
//   通过平台相关的系统调用(如:epoll,select)等对关心的事件进行监控
//   当关心的事件发生且处理完成,则调用其回调函数
size_t do_one(boost::asio::detail::mutex::scoped_lock& lock,
      idle_thread_info* this_idle_thread, boost::system::error_code& ec)
{
  // 如果没有未完成的工作并且service没有被用户叫停,则立即返回,结束事件循环
  // outstanding_work_(未完成的工作)由两种类型:一种是由io_service::work封装的工作(read, write, timer);另一种是回调
  if (outstanding_work_ == 0 && !stopped_)
  {
    stop_all_threads(lock);
    ec = boost::system::error_code();
    return 0;
  }

  //是否采用轮询机制
  bool polling = !this_idle_thread;
  bool task_has_run = false;
  while (!stopped_)
  {
    // 处理器队列不为空,处理器队列中保存两种类型的handler:
    //   一种task_handler_,在适当的时候被加入到处理器队列,用于表示是否需要进行事件监控;
    //   另一种则是事件的回调
    if (!handler_queue_.empty())
    {
      // Prepare to execute first handler from queue.
      // 准备执行处理器队列中的第一个处理器
      handler_queue::handler* h = handler_queue_.front();
      // 将取出的处理器从处理器队列中移除
      handler_queue_.pop();

			// 如果处理器为task_handler_,表示需要进行事件监控处理
      if (h == &task_handler_)
      {
      	// 处理器队列中是否有其它的处理器
        bool more_handlers = (!handler_queue_.empty());
        task_interrupted_ = more_handlers || polling;

        // If the task has already run and we're polling then we're done.
        if (task_has_run && polling)
        {
          task_interrupted_ = true;
          handler_queue_.push(&task_handler_);
          ec = boost::system::error_code();
          return 0;
        }
        task_has_run = true;

        lock.unlock();
        task_cleanup c(lock, *this);

        // Run the task. May throw an exception. Only block if the handler
        // queue is empty and we have an idle_thread_info object, otherwise
        // we want to return as soon as possible.
        // 运行事件监控,参数表示是否阻塞当前线程
        task_->run(!more_handlers && !polling);
      }
      else
      {
        lock.unlock();
        handler_cleanup c(lock, *this);

        // Invoke the handler. May throw an exception.
        // 调用事件回调函数
        h->invoke(); // invoke() deletes the handler object

        ec = boost::system::error_code();
        return 1;
      }
    }
    else if (this_idle_thread)
    {
      // Nothing to run right now, so just wait for work to do.
      this_idle_thread->next = first_idle_thread_;
      first_idle_thread_ = this_idle_thread;
      this_idle_thread->wakeup_event.clear(lock);
      this_idle_thread->wakeup_event.wait(lock);
    }
    else
    {
      ec = boost::system::error_code();
      return 0;
    }
  }

  ec = boost::system::error_code();
  return 0;
}


### 关于 `clmdep_asio::detail::call_stack` 报错的分析 #### 背景说明 `clmdep_asio::detail::call_stack` 是 Asio 库中的一个内部实现细节,用于跟踪当前线程上的调用栈状态。它通常被用来管理异步操作的状态以及确保某些操作的安全性。例如,在多线程环境中,Asio 的 `strand` 提供了一种机制来序列化访问共享资源的操作。 从提供的引用来看,问题可能涉及以下几个方面: 1. **`io_context_impl` 类型定义** 如果编译器选择了基于 IOCP(Windows I/O Completion Port)的实现,则会使用 `win_iocp_io_context` 作为底层实现[^1]。这表明程序运行在 Windows 平台上,并依赖于完成端口模型来进行高效的异步 I/O 处理。 2. **`strand::post()` 方法的行为** 当通过 `strand::post()` 发送任务时,实际的任务会被传递给服务对象并排队执行[^2]。需要注意的是,这里的分配器参数 `(void)a;` 表明其作用可能是为了兼容 STL 容器或其他内存管理工具,但实际上并未真正利用到该参数。 3. **模拟 I/O 事件与实际处理逻辑** 使用 `PostQueuedCompletionStatus` 函数可以手动触发完成端口的通知机制[^3]。这意味着即使没有真实的硬件中断发生,也可以人为制造出类似于设备完成工作的效果。这种技术常用于单元测试或者调试目的。 4. **Strand 的典型应用场景** Strands 可以帮助保护共享数据免受并发修改的影响。通过将多个异步操作绑定到同一个 strand 上,能够保证这些操作按照一定的顺序依次被执行而不是同时竞争锁资源[^4]。 5. **潜在错误来源——非法地址访问** 堆栈信息指出最终崩溃的原因是因为尝试访问了一个无效的内存位置[^5]。进一步推测可知,这种情况很可能发生在某个未初始化的对象身上或者是由于释放后的指针再次被引用所引起。 --- #### 解决方案建议 针对上述背景描述,以下是几种可能解决问题的方法及其原理解释: ##### 方法一:验证 Strand 配置是否正确 确认创建 `boost::asio::io_service::strand` 实例时传入的有效性。如果 io_service 对象本身已经销毁或处于不可用状态,则任何对其关联 strands 的操作都可能导致未定义行为。 ```cpp // 正确做法 boost::asio::io_service io; auto strand = boost::make_shared<boost::asio::io_service::strand>(&io); timer.async_wait([=](const boost::system::error_code& ec){ /*...*/ }); ``` ##### 方法二:检查回调函数上下文中是否存在越界风险 仔细审查所有涉及到动态数组、字符串或者其他容器类型的数据结构操作部分。特别留意那些可能会超出范围索引的情况。 ```cpp std::vector<int> vec{1, 2}; try { int value = vec.at(10); // 这里会产生 out_of_range exception } catch(const std::out_of_range& e){ std::cerr << "Error: " << e.what() << '\n'; } ``` ##### 方法三:排查是否有悬垂指针现象存在 一旦某块内存区域被删除之后再继续对其进行读写就会引发严重的后果。因此务必保持良好的生命周期管理习惯。 ```cpp class MyClass {}; MyClass* ptr = new MyClass(); delete ptr; // 下面这段代码会造成 undefined behavior (*ptr).someMethod(); ``` ##### 方法四:启用更严格的编译选项以便尽早发现问题 现代 C++ 编译器提供了丰富的诊断功能可以帮助开发者发现隐藏缺陷。比如 GCC/Clang 中可以通过 `-Wall -Wextra -pedantic-errors` 参数开启全面警告模式;而在 MSVC 则对应 `/W4 /WX` 开关组合形式。 --- ### 结论总结 综上所述,此次 clmdep_asio::detail::call_stack 报错的根本原因极有可能源于以下几点之一: - 错误配置了 strand 导致后续一系列连锁反应; - 存在于业务逻辑里的边界条件判断失误从而间接影响到底层框架表现; - 或者干脆就是简单的野指针滥用案例而已。 无论如何都需要逐一排除以上可能性直至找到确切答案为止!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值