2012-07-20 70 views
4

在我们的应用程序中,我们使用Boost库(和ASIO进行网络通信)。最近,我们发现如果我们通过同一套接口从不同线程发送数据,我们的客户端应用程序正在接收垃圾数据。从不同线程写入boost :: asio socket

小试,以突出的问题:

#include <stdio.h> 
#include <boost/thread.hpp> 
#include <boost/asio.hpp> 

void send_routine(boost::shared_ptr<boost::asio::ip::tcp::socket> s, char c) 
{ 
    std::vector<char> data(15000, c); 
    data.push_back('\n'); 

    for (int i=0; i<1000; i++) 
    boost::asio::write(*s, boost::asio::buffer(&data[0], data.size())); 
} 


int main() 
{ 
    using namespace boost::asio; 
    using namespace boost::asio::ip; 

    try { 
    io_service io_service; 
    io_service::work work(io_service); 

    const char* host = "localhost"; 
    const char* service_name = "18000"; 

    tcp::resolver resolver(io_service); 
    tcp::resolver::query query(tcp::v4(), host, service_name); 
    tcp::resolver::iterator iterator = resolver.resolve(query); 

    auto socket = boost::shared_ptr<tcp::socket>(new tcp::socket(io_service)); 
    socket->connect(*iterator); 

    boost::thread t1(send_routine, socket, 'A'); 
    boost::thread t2(send_routine, socket, 'B'); 
    boost::thread t3(send_routine, socket, 'C'); 

    t1.join(); 
    t2.join(); 
    t3.join(); 
    } 
    catch (std::exception& e) { 
    printf("FAIL: %s\n", e.what()); 
    } 
    return 0; 
} 

所以,我们在这里创建插座,连接到localhost:18000并启动3个线程将写入套接字。

在不同的终端窗口中,我运行了nc -l -p 18000 | tee out.txt | sort | uniq | wc -l。我期望3作为输出,但它会在网络流中返回多于100个“不同的字符串”(因此数据已损坏)。但它适用于小缓冲区大小(例如,如果我们将15000更改为80)。

所以,问题是:这是ASIO库的正确行为吗?另一个:如何解决它?我应该在我的send_routine函数中使用mutex(还是有另一种解决方案)?

回答

2

那么根据documentationtcp::socket多个线程之间共享时不是线程安全的。
因此,您要么像您建议的boost::mutex一样进行同步,要么使用异步写入。 io_service为你工作。

+7

'async_write'不会神奇地让这个问题消失。不同缓冲区的写入仍然需要串行发生,即使它是通过异步IO完成的。 – Chad 2012-07-20 15:46:59

+0

'io_service'应该为你序列化。 – berkus 2014-11-11 17:56:51

+1

async_write由多个async_send组成,没有进一步的线程安全性(我研究了实现)。所以当从不同的线程使用相同的套接字时它不是线程安全的。 – Databyte 2015-06-07 12:22:16

0

您可能会遇到两个问题,线程问题可以通过例如一个线程专用于写入来解决,另一个线程可以通过一个队列发送响应。您也可以将您的设计更改为异步设计,并使用write_some()函数,并让线程由io_service :: run()完成,该线程可以由多个线程运行。

其次,如果客户希望以相同顺序回答问题,那么您可能会遇到协议问题。

心连心

托斯滕

5

writeasync_write在您使用它们的方式中不是线程安全的。解决这个问题的标准方法是排列你的消息,然后一次一个写出来。

2

是的,还有另一种解决方案! Strands: Use Threads Without Explicit Locking。要小心,只为“事件处理程序”提供对套接字的“原子”访问,当然,您需要使用asio“事件处理程序”,而不是代码的情况。换句话说,你需要使用boost :: asio :: async_write而不是boost :: asio :: write。

+0

请注意,只有链接的答案是不鼓励的,所以答案应该是搜索解决方案的终点(而另一个引用的中途停留时间往往会过时)。请考虑在此添加独立的摘要,并将链接保留为参考。 – kleopatra 2013-07-24 13:30:41

相关问题