【问题标题】:Incorrect use of boost::asio and boost::threadboost::asio 和 boost::thread 的错误使用
【发布时间】:2011-09-28 15:04:51
【问题描述】:

我正在使用 boost::asioboost::thread 来实现一个接受消息的消息服务,如果有则异步发送如果有消息正在处理,则没有消息正在处理或排队消息。

消息速率在我看来很高,大约 每秒 2.000 条消息。有这么多消息,我面临损坏的消息,但很少见。在 2.000 条消息中,大约 4-8 条已损坏。我认为问题是由于不正确使用 boost::asio 和/或 boost::thread 库造成的。

我实现的代码主要基于this boost tutorial。我找不到错误,而且由于主要信息已经解决,我很难缩小问题范围。

也许其他人知道这里出了什么问题?

基本上这个类的使用方式如下:

(1) 在我的程序开始时调用构造函数以启动线程,从而接受和传输消息的服务

(2) 每当我想传输消息时,我都会调用MessageService::transmitMessage(),它将async_write 的任务委托给处理消息队列的线程。

using namespace google::protobuf::io;
using boost::asio::ip::tcp;

MessageService::MessageService(std::string ip, std::string port) :
    work(io_service), resolver(io_service), socket(io_service) {

    messageQueue = new std::deque<AgentMessage>;
    tcp::resolver::query query(ip, port);
    endpoint_iterator = resolver.resolve(query);

    tcp::endpoint endpoint = *endpoint_iterator;

    socket.async_connect(endpoint, boost::bind(&MessageService::handle_connect,
            this, boost::asio::placeholders::error, ++endpoint_iterator));

    boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
}

void MessageService::await() {

    while (!messageQueue->empty()) {

        signal(SIGINT, exit);

        int messagesLeft = messageQueue->size();
        sleep(3);
        std::cout << "Pending Profiler Agents Messages: "
                << messageQueue->size() << std::endl;
        if (messagesLeft == messageQueue->size()) {
            std::cout << "Connection Error" << std::endl;
            break;
        }
    }

    std::cout << i << std::endl;
}

void MessageService::write(AgentMessage agentMessage, long systemTime,
        int JVM_ID) {
    agentMessage.set_timestamp(Agent::Helper::getCurrentClockCycle());
    agentMessage.set_jvm_id(JVM_ID);
    agentMessage.set_systemtime(systemTime);
    io_service.post(boost::bind(&MessageService::do_write, this, agentMessage));
}

void MessageService::do_close() {
    socket.close();
}

void MessageService::transmitMessage(AgentMessage agentMessage) {

    ++i;

    boost::asio::streambuf b;
    std::ostream os(&b);

    ZeroCopyOutputStream *raw_output = new OstreamOutputStream(&os);
    CodedOutputStream *coded_output = new CodedOutputStream(raw_output);

    coded_output->WriteVarint32(agentMessage.ByteSize());
    agentMessage.SerializeToCodedStream(coded_output);

    delete coded_output;
    delete raw_output;

    boost::system::error_code ignored_error;

    boost::asio::async_write(socket, b.data(), boost::bind(
            &MessageService::handle_write, this,
            boost::asio::placeholders::error));
}

void MessageService::do_write(AgentMessage agentMessage) {

    bool write_in_progress = !messageQueue->empty();
    messageQueue->push_back(agentMessage);

    if (!write_in_progress) {
        transmitMessage(agentMessage);
    }
}

void MessageService::handle_write(const boost::system::error_code &error) {

    if (!error) {
        messageQueue->pop_front();
        if (!messageQueue->empty()) {
            transmitMessage(messageQueue->front());
        }
    } else {
        std::cout << error << std::endl;
        do_close();
    }
}

void MessageService::handle_connect(const boost::system::error_code &error,
        tcp::resolver::iterator endpoint_iterator) {
    // can be used to receive commands from the Java profiler interface
}

MessageService::~MessageService() {
    // TODO Auto-generated destructor stub
}

头文件:

    using boost::asio::ip::tcp;

class MessageService {
public:
    MessageService(std::string ip, std::string port);
    virtual ~MessageService();
    void write(AgentMessage agentMessage, long systemTime, int JVM_ID);
    void await();

private:
    boost::asio::io_service io_service;
    boost::asio::io_service::work work;
    tcp::resolver resolver;
    tcp::resolver::iterator endpoint_iterator;
    tcp::socket socket;
    std::deque<AgentMessage> *messageQueue;

    void do_write(AgentMessage agentMessage);

    void do_close();

    void handle_write(const boost::system::error_code &error);

    void handle_connect(const boost::system::error_code &error,
            tcp::resolver::iterator endpoint_iterator);

    void transmitMessage(AgentMessage agentMessage);
};

【问题讨论】:

  • 既然你提到boost::thread我只能假设你在程序中使用了多个线程,但是你没有锁定来确保访问消息队列(我假设这是只有共享资源)是安全的——我必须承认我没有通过代码,所以可能对队列的访问是单线程的......你告诉我
  • 我认为只有一个线程处理消息和队列。由于唯一的功能,至少我认为是这种方式,由多个线程调用的是transmitMessage和transmitMessage委托: io_service.post(boost::bind(&MessageService::do_write, this, agentMessage));我认为仅由构造函数中启动的一个线程执行。但也许我错了。
  • transmitMessage 不会被多个线程调用,您在 io 服务上只有一个线程调度。我看不出上面的代码有什么问题(只要你只从另一个线程调用write——比如主线程)——我会检查接收器代码。
  • 哦,我的意思是write 是的,这个函数是从主线程调用的,也可能是从其他线程调用的。
  • 我想我错了。在堆栈上创建 boost::asio::streambuf 对我来说似乎不正确!.

标签: c++ sockets boost boost-asio boost-thread


【解决方案1】:

我觉得这个方法很可疑

void MessageService::transmitMessage(AgentMessage agentMessage) {
    ++i;

    boost::asio::streambuf b;
    std::ostream os(&b);

    ZeroCopyOutputStream *raw_output = new OstreamOutputStream(&os);
    CodedOutputStream *coded_output = new CodedOutputStream(raw_output);

    coded_output->WriteVarint32(agentMessage.ByteSize());
    agentMessage.SerializeToCodedStream(coded_output);

    delete coded_output;
    delete raw_output;

    boost::system::error_code ignored_error;

    boost::asio::async_write(socket, b.data(), boost::bind(
            &MessageService::handle_write, this,
            boost::asio::placeholders::error));
}

您似乎正在将 AgentMessage(应该通过 const 引用传递)序列化为 streambuf。然而,在调用 async_write 完成处理程序之前,不能保证此序列化数据存在,这在 async_write documentation 中明确描述

缓冲区

一个或多个包含要写入的数据的缓冲区。 虽然缓冲区对象可能是 根据需要复制,所有权 底层内存块被保留 由调用者,它必须保证 他们一直有效,直到 处理程序被调用。

要解决此问题,请确保缓冲区保持在范围内,直到调用完成处理程序。一种方法是将缓冲区作为参数传递给有界完成处理程序:

boost::asio::async_write(socket, b.data(), boost::bind(
            &MessageService::handle_write, this,
            boost::asio::placeholders::error,
            coded_output
            // ^^^ buffer goes here
            ));

然后从完成处理程序中删除它。我建议您也考虑使用 shared_ptr 而不是裸指针。

【讨论】:

  • 所以由于 async_write 立即返回,但不知道 async_write 何时完成并留下了传输消息,因此可能不确定分配的缓冲区会发生什么?你有什么建议来解决这个问题?
  • @platzhirsch async_write 完成的时间是众所周知的,这是调用完成处理程序的时间。我已经用一种可能的解决方案更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-06
  • 1970-01-01
  • 2013-03-18
  • 1970-01-01
  • 2017-09-02
  • 2019-02-21
  • 2019-10-16
相关资源
最近更新 更多