0

我正在使用 async_read_some 从保存在名为 _data 的 char[] 的端口中读取数据。它的缓冲区大小对于每个请求都足够大:

void start() {

    socket_.async_read_some(boost::asio::buffer(data_,BUFFERSIZE),make_custom_alloc_handler(allocator_,boost::bind(&attentionSession::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));

}

void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {

    string ip = socket_.remote_endpoint().address().to_string();
    log->processData(data_,ip,"memory");
    strcpy(data_,"");

}

processData 通过将请求复制到另一个新分配的 char* 来向请求添加一些附加信息(如时间戳等)。然后将此 char[] 发送到 writeToMemory(char*) 以将该 char* 附加到 std::string memoryBuffer:

void writeCacheToFile() {

    // This function writes buffer data to the log file

    char* temp = new char[memoryBuffer.length() + 1];
    strcpy(temp, memoryBuffer.c_str());
    writeToFile(temp);
    delete[] temp;
    memoryBuffer.clear();

}

void writeToMemory(char* data) {

    int maxSize = 1024;

     // Checks if char* data would 'fit' into the pre-defined maxSize

    if ((strlen((const char*)data) + memoryBuffer.length()) >= maxSize) {
        writeCacheToFile(); // If not the cache memoryBuffer is saved first
    }

    memoryBuffer.append((const char*) data);

    cout << memoryBuffer.length() << endl;

}

它可以工作,但是如果不断有请求(用请求轰炸它),事情就会变得一团糟。正如您在上面的 writeToMemory() 函数中看到的那样,我将添加一行来打印 memoryBuffer 的当前长度,这就是我认为它与 std::strings 的线程安全有关的地方:

96
188
284
3639
94
190
286
2591
102
198
294
388
484
2591
96
2591
96
190
286
2591

每个(由 processData() 处理)请求的长度为 96 个字符。但是这里 memoryBuffer 的长度只是上升和下降 - 有些长度甚至大于 maxSize (1024个字符)。

编辑:山姆指出我应该添加更多代码。这就是我启动 io_service 的方式:

boost::asio::io_service ioService;
boost::scoped_ptr<boost::thread> ioServiceThread; 
server_class server (ioService,PORT); // Create server instance
ioServiceThread.reset (new boost::thread ( boost::bind ( &boost::asio::io_service::run, &ioService  ) ) ); 
// Only one threaded io_service (to receive user inputs in main() function)

这是完成请求后 async_acceptor 的功能:

typedef boost::shared_ptr<session_class> session_ptr;

void handleAccept(session_ptr thisSession, const boost::system::error_code& error) {
    if (!error) {
      thisSession->start(); // Calls the start() function above
      thisSession.reset(new session(ioService,LOGGING_CLASS));
      acceptor.async_accept(thisSession->socket(),boost::bind(&server_class::handleAccept, this, thisSession, PLACEHOLDER_ERROR));
    }
 }

session_class 包含上面提到的函数 start() 和 handle_read(x,y)。LOGGING_CLASS 提供了写入日志文件的类(包含函数 writeCacheToFile() 和 writeToMemory(char*))。log(上面提到的)是此类的一种。

EOE:编辑结束

如果我尝试使用 boost::threads 修复外包缓存部分(将接收到的 char* 附加到 std::string),它最终会完全混淆 memoryBuffer

它真的是 std::strings 的线程安全还是我错过的其他东西?

提前感谢您的帮助!:)

保罗

4

3 回答 3

1

我提出了与评论相同的观点,但我认为值得将其扩展为答案。发布您拥有的代码片段并没有太大帮助,它们并没有给我们完整的画面。例如,我不清楚以下概念:

  1. 您没有检查处理程序bytes_transferred中的参数async_read_some。这非常重要,因为即使您告诉它读取n字节,它也可以在读取字节时返回n - xwhere x <= n。正如文档所述,您应该考虑使用组合操作之一,例如async_read free 函数
  2. 您正在为异步读取操作使用自定义内存分配,大概基于提供的示例。你为什么需要那个?
  3. 缓冲寿命。async_read例如:在调用处理程序之前,您的缓冲区是否保持在范围内?
  4. 对象生命周期。例如:您使用shared_ptr得当吗?io_service它的整个事件循环是否在范围内?
  5. 您是每个进程使用一个io_service还是每个线程使用一个?
  6. 为什么需要线程?通常,首先在单线程上下文中更容易理解异步编程。

在使用 Boost.Asio 时,所有这些都是非常重要的概念。调试的一部分是将感知到的问题归结为更小的复制器。这对 Stack Overflow 和成为一名优秀的程序员都很有用。它将帮助您了解问题,并帮助我们帮助您找到问题。我强烈建议您花一些精力制作一个可以编译的较小的可重现示例。如果这不可能,请在证明单线程方案有效之后才考虑使用多线程。

于 2011-02-26T16:59:39.373 回答
1

好吧,问题不是 Boost 也不是 Boost::Asio。这是我测试我的应用程序的方法:

我使用 nc (Netcat) 通过这个命令测试我的应用程序的性能和功能

nc localhost 4450 <testfile

其中 testfile 包含一个 36 个字符长的测试字符串。Netcat 不仅速度慢 - 它是这个问题的根源

在我改变了策略并编写了一点 boost::asio 应用程序以向我的应用程序发送相同的请求后,它就可以工作了。快速、简单且没有任何问题。

我吸取了教训:永远不要再使用 Netcat 进行压力测试!

于 2011-02-26T22:51:59.747 回答
0

我要做的第一件事是重构调用 async_read_some 的行。它是如此之长,几乎不可能看到正在发生的事情。

我的猜测是,当您敲击此操作时,您将在旧调用返回之前对 handle_read 进行新调用。handle_read 不是线程安全的。除了别的,

strcpy(data_,"");

当多个副本都试图复制到data_中时会给你带来麻烦。

您需要使 handle_read 线程安全。

于 2011-02-23T21:53:45.150 回答