2012-07-20 68 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

相關問題