[cocos2d-x]CCHttpClient的一個bug,cchttpclient

來源:互聯網
上載者:User

[cocos2d-x]CCHttpClient的一個bug,cchttpclient

    公司的新遊戲《我是大官人》馬上就要大規模PR了,一切都已經準備就緒,這時測試部門卻反饋了一個小問題,開啟遊戲的時候,偶爾會卡在啟動介面,提示:正在串連伺服器...然後就沒反應了,這個問題發生的機率很低,大概3%左右,而且退出重新開啟遊戲就好了,“應該是網路不好造成的”,大家並沒有太重視這個bug,但是老闆不放心,“就算是網路問題,也不應該卡住,如果是新玩家碰到這種情況就直接流失了,這個問題得查一下。” 看來這不是一個小問題,於是這個bug分配給了我。

    花了一些時間重現,我發現問題出在CCHttpClient,熟悉cocos2d-x的同學知道,這是一個非同步網路程式庫,它的工作原理是這樣的:HttpClient內部有一個背景工作執行緒,遊戲主線程調用send()函數,將http請求壓入一個隊列,然後喚醒背景工作執行緒就返回了。背景工作執行緒被喚醒後,將http請求從隊列中取出,再調用libcurl進行處理,然後再通過回調將結果返回主線程。bug重現的時候,主線程註冊的回調沒有觸發,遊戲就卡住了。我估計是libcurl將背景工作執行緒卡住了,於是在背景工作執行緒中加入了一些列印進行驗證,結果卻大意料:背景工作執行緒根本就沒有觸發!

    又是一個多線程同步的問題!這是背景工作執行緒的程式碼片段:

// Worker threadstatic void* networkThread(void *data){        CCHttpRequest *request = NULL;        while (true)     {        if (need_quit)        {            break;        }                // step 1: send http request if the requestQueue isn't empty        request = NULL;                pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue        if (0 != s_requestQueue->count())        {            request = dynamic_cast<CCHttpRequest*>(s_requestQueue->objectAtIndex(0));            s_requestQueue->removeObjectAtIndex(0);              // request's refcount = 1 here        }        pthread_mutex_unlock(&s_requestQueueMutex);                if (NULL == request)        {        // Wait for http request tasks from main thread        pthread_cond_wait(&s_SleepCondition, &s_SleepMutex);            continue;        }                // step 2: libcurl sync access

背景工作執行緒閒置時候,通過pthread_cond_wait()掛起

send()函數的代碼:

//Add a get task to queuevoid CCHttpClient::send(CCHttpRequest* request){        if (false == lazyInitThreadSemphore())     {        return;    }        if (!request)    {        return;    }            ++s_asyncRequestCount;        request->retain();            pthread_mutex_lock(&s_requestQueueMutex);    s_requestQueue->addObject(request);    pthread_mutex_unlock(&s_requestQueueMutex);        // Notify thread start to work    pthread_cond_signal(&s_SleepCondition);}

我仔細的看了一遍,發現這句話很奇怪:

pthread_cond_wait(&s_SleepCondition, &s_SleepMutex);

這裡用了一個互斥鎖s_SleepMutex,而這個鎖沒有其他代碼使用。這很可能是一個錯誤:一個互斥鎖如果只有一處代碼使用,那麼只有一種可能,這段代碼會在多個線程中執行。而httpClient的線程顯然只有一個。google了一番,果然是這個互斥鎖用錯了。大家可以參考知乎上的一篇文章:http://www.zhihu.com/question/24116967 

裡面詳細解釋了為什麼沒有正確的加鎖會導致訊號量丟失。


這裡是修改後的代碼,不再使用單獨的s_SleepMutex, 用requestQueueMutex代替

// Worker threadstatic void* networkThread(void *data){        CCHttpRequest *request = NULL;        while (true)     {        if (need_quit)        {            break;        }                // step 1: send http request if the requestQueue isn't empty        request = NULL;        pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue        while (0 == s_requestQueue->count()) {            pthread_cond_wait(&s_SleepCondition, &s_requestQueueMutex);        }        request = dynamic_cast<CCHttpRequest*>(s_requestQueue->objectAtIndex(0));        s_requestQueue->removeObjectAtIndex(0);                // request's refcount = 1 here                    CCLog("s_SleepCondition notified");        pthread_mutex_unlock(&s_requestQueueMutex);        

反覆測試了一個下午,這個bug沒有再複現了。


這是在cocos2d-x 2.1.4版本上改的,cocos2d-x 3.x沒有使用pthread_cond_wait進行線程同步,但也存在類似的問題。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.