單生產者-多消費者模型中遇到的問題

來源:互聯網
上載者:User

標籤: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:37220
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………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;


 


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.