写一段时间后,boost :: async_write失败了

时间:2011-05-26 19:05:34

标签: c++ networking boost boost-asio

我有一个非常奇怪的问题。我编写了一个服务器,它将从第三方接收的数据写入连接的客户端。服务器暂时写入客户端,但过了一段时间,async_write失败或写入永远不会返回。对于我的程序,如果async_write永远不会返回,则不会发生后续写入,我的服务器将排队从第三方收到的数据,直到所有内容都爆炸。

我在下面提供了我的代码:

void ClientPartitionServer::HandleSignal(const CommonSessionMessage& message, int transferSize) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(m_clientSockets.size() != 0) {
    TransferToQueueBuffer(message.GetData(), transferSize);
  }
  if(m_writeCompleteFlag) {
    // TransferToWriteBuffer();
    for(vector<boost::asio::ip::tcp::socket*>::const_iterator i = m_clientSockets.begin(); i != m_clientSockets.end(); ++i) {
      WriteToClient(*i);
    }
  }
}

void ClientPartitionServer::WriteToClient(boost::asio::ip::tcp::socket* clientSocket) {
  m_writeCompleteFlag = false;
  cout << "Iniating write: " << m_identifier << endl;
  boost::asio::async_write(
    *clientSocket,
    boost::asio::buffer(m_queueBuffer.get(), m_queueBufferSize),
    boost::bind(
      &ClientPartitionServer::HandleWrite, this,
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred
  ));
}

void ClientPartitionServer::HandleWrite(const boost::system::error_code& ec, size_t bytes_transferred) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(ec != 0) {
    cerr << "Error writing to client: " << ec.message() << " " << m_identifier << endl;
    // return;
    cout << "HandleWrite Error" << endl;
    exit(0);
  }
  cout << "Write complete: " << m_identifier << endl;
  m_writeCompleteFlag = true;
  m_queueBuffer.reset();
  m_queueBufferSize = 0;
}

任何帮助都将不胜感激。

谢谢。

3 个答案:

答案 0 :(得分:4)

在没有看到所有代码的情况下很难说,但是对于我来说,如果你在多个(甚至一个)WriteToClient个调用中持有互斥锁,那将是一个危险信号。通常在I / O上保持任何类型的锁(即使是像这里一样的异步)也不利于性能,最坏的情况是加载时出现奇怪的死锁。如果异步写入完成内联并且您在同一个线程/调用堆栈中的HandleWrite上回调,会发生什么?

我会尝试重构这个,以便在写入调用期间释放锁。

无论解决方案是什么,更一般的建议:

  • 不要锁定I / O
  • 添加一些诊断输出 - 什么 线程调用每个处理程序,并在 什么顺序?
  • 点击后尝试调试 静态。应该可以 从中诊断死锁 过程状态。

答案 1 :(得分:1)

使用strand来序列化对特定连接对象的访问。特别是,请查看strand::wrap()。要查看使用线索的其他示例,请查看few different timer examples(虽然该代码适用于任何async_*()来电)。

答案 2 :(得分:0)

首先,我不同意这些注释表明在异步操作中持有锁是一个问题。

持有锁:

  1. 任何调用回调的函数都不好。

  2. 任何阻止操作都很糟糕。

  3. async_write明确保证既不阻止也不调用处理程序,所以握住锁定对我来说很好。

    但是,我可以在您的代码中看到违反async_write的其他要求的错误。在调用完成处理程序之前,不允许调用async_write。这就是你违反的行为。

    每当其中一个处理程序被调用时,m_writeCompleteFlag就会设置为true。这意味着您可能违反高负荷下其他一些N-1插座的async_write规则。