標籤:c++ socket
(1) 原始代碼
最近使用單生產者-多消費者模型是遇到一個問題,以前既然都沒有想到過。生產者線程的代碼如下,準系統就是接收到一個串連之後建立一個Socket對象並放到list中等待處理。
void DataManager::InternalStart() { server_socket_ = new ServerSocket(); if (!server_socket_->SetAddress(NetworkUtil::GetIpAddress().c_str(), 9091)) { LOG(ERROR) << "Set address failed."; delete server_socket_; server_socket_ = NULL; return; } server_socket_->SetSoBlocking(true); if (!server_socket_->Listen()) { LOG(ERROR) << "listen failed."; return; } Socket *socket = NULL; while (!stop_) { if ((socket = server_socket_->Accept()) != NULL) { LOG(INFO) << "Recieved connection fd: " << socket->GetAddr(); { common::MutexLock lc(&socket_mu_); socket_list_.push_back(socket); cond_var_.Signal(); } } }
多個消費者線程的的代碼如下,準系統是從list中取得一個Socket對象進行處理;
void DataManager::WorkEntry() { Socket *socket = NULL; while (!stop_) { // Get connection socket. { common::MutexLock lc(&socket_mu_); if (socket_list_.empty()) { cond_var_.Wait(&socket_mu_); } if (stop_) break; socket = socket_list_.front(); socket_list_.pop_front(); } bool success = false; do{ { Packet request; if ((success = socket->GetPacket(&request))) { HandlePacket(&request); } } } while (success); delete socket; socket = NULL; }}
(2) 問題
運行過程中進場出現段錯誤,都是在12行(socket = socket_list_.front())。使用GDB調試發現socket_list_的size為0。
(3) 加入log調試
加入下面的log進行調試
@@ -115,6 +115,7 @@ voidDataManager::InternalStart() { { common::MutexLocklc(&socket_mu_); socket_list_.push_back(socket);+ LOG(INFO) << "1:size: " << socket_list_.size(); cond_var_.Signal(); } }@@ -129,11 +130,14 @@ voidDataManager::WorkEntry() { common::MutexLock lc(&socket_mu_); if (socket_list_.empty()) { cond_var_.Wait(&socket_mu_);+ LOG(INFO) << "2:size: " << socket_list_.size(); } if (stop_) break;+ LOG(INFO) << "3: size: " << socket_list_.size(); socket = socket_list_.front(); socket_list_.pop_front();+ LOG(INFO) << "4: size:" << socket_list_.size();
列印的log如下:
I0809 02:35:45.269896 17305DataManager.cc:114] Recieved connection fd: 10.237.92.30:37220I0809 02:35:45.269902 17305DataManager.cc:118] 1: size: 1I0809 02:35:45.269928 17310DataManager.cc:133] 2: size: 1I0809 02:35:45.269935 17310DataManager.cc:137] 3: size: 1I0809 02:35:45.269937 17310DataManager.cc:140] 4: size: 0………I0809 02:35:45.271636 17305 DataManager.cc:114]Recieved connection fd: 10.237.92.30:37224I0809 02:35:45.271644 17305DataManager.cc:118] 1: size: 1I0809 02:35:45.271663 17310DataManager.cc:137] 3: size: 1I0809 02:35:45.271670 17310DataManager.cc:140] 4: size: 0I0809 02:35:45.271739 17309 DataManager.cc:133]2: size: 0I0809 02:35:45.271750 17309DataManager.cc:137] 3: size: 0
(4) 分析:a) 正常的log順序
正常的log順序應該是,add一個Socket之後得到,有一個消費者線程被signal喚醒並處理這個socket。
I0809 02:35:45.269902 17305DataManager.cc:118] 1: size: 1I0809 02:35:45.269928 17310DataManager.cc:133] 2: size: 1I0809 02:35:45.269935 17310DataManager.cc:137] 3: size: 1I0809 02:35:45.269937 17310DataManager.cc:140] 4: size: 0
b) 出錯時的log順序
出現錯誤時的log順序如下,
I0809 02:35:45.271644 17305DataManager.cc:118] 1: size: 1I0809 02:35:45.271663 17310DataManager.cc:137] 3: size: 1I0809 02:35:45.271670 17310DataManager.cc:140] 4: size: 0I0809 02:35:45.271739 17309 DataManager.cc:133]2: size: 0I0809 02:35:45.271750 17309DataManager.cc:137] 3: size: 0
線程號可以從第三列得出, 17305的線程是生產者線程,17310和17309為消費者線程。從列印的log可以看除啟動並執行順序如下:
a) 初始狀態; i. 17305:獲得socket_mu_準備向socket_list_中插入socket。 ii. 17309:正處於cond_var_.Wait(&socket_mu_);狀態下等待cond_var發生; iii. 17310 :socket_mu_應該是在試圖b) 17305線程調用cond_var_.Signal()喚醒17309,此時17309和17310還需要爭奪socket_mu_,應該是17310先得到了socket_mu_所以17309必須再次睡眠。c) 17310將剛才17305生產的socket消耗了,並且釋放了socket_mu_。但是此時的socket_list_有變成空的了。d) 17309得到socket_mu_,調用socket_list_.front()時程式crash。
(4) 解決辦法:多加一個判斷。
@@ -129,6 +129,9 @@ voidDataManager::WorkEntry() { common::MutexLock lc(&socket_mu_); if (socket_list_.empty()) { cond_var_.Wait(&socket_mu_);++ if (socket_list_.empty())+ continue; } if (stop_) break;