我在引擎的設計裡,修修改改的弄出了幾種執行緒函式的架構。
一開始是一個thread loop,執行緒函式中有一個無窮迴圈,像是我在Render Thread做的這樣,
DWORD WINAPI CRenderThread::RenderThreadFunc(LPVOID thread_obj)
{
CRenderThread* thread_ptr=(CRenderThread*)(thread_obj);
while (!s_bExitThread)
{
CRenderCommand* cmd=0;
thread_ptr->m_CommandQueueLock.Lock();
if (thread_ptr->m_CommandQueue.size())
{
cmd=thread_ptr->m_CommandQueue.front();
thread_ptr->m_CommandQueue.pop_front();
}
thread_ptr->m_CommandQueueLock.Unlock();
if (cmd)
{
cmd->Execute(thread_ptr->m_pRenderThreadState);
medelete cmd;
}
Sleep(0); // 這個很重要,如果不休息,主緒會忽快忽慢,而且可能兩次執行的速度差異幾十倍
}
return 1;
}
迴圈的最後,要讓執行緒睡一下下,因為如果不這麼做的話,在Windows的架構裡,這個執行緒可是會吃掉CPU不肯放出來的,也就會造成主緒所分配到的時間忽多忽少,跑得忽快忽慢。
另一種執行緒函式,是用在背景讀檔的執行緒,每個執行緒只執行一次。
DWORD WINAPI CThreadStream::LoadingThreadFunc(LPVOID thread_obj)
{
CThreadStream* thread_ptr=(CThreadStream*)(thread_obj);
WaitForSingleObject(thread_ptr->m_BeginEvent,INFINITE); // wait for begin
thread_ptr->Load(thread_ptr->m_szRequestFilename,
thread_ptr->m_szRequestPathID);
SetEvent(thread_ptr->m_EndEvent); // loading done
return 1;
}
執行緒建立之後,並不會馬上進行讀檔,而是會等待一個開始的事件,讀檔完成以後,再設定結束事件,讓主緒可以得知檔案已經讀完。
這個設計,有個問題。也就是每讀取一個檔案,我們就必須建立一個執行緒,讀完了,就刪除執行緒。這在系統資源的使用上,還有執行的效能上,都不是很好的設計方式。
我沒有用Thread Pool來改進,而是做了其他方向的修改。
最先是用了一個無窮迴圈來做。
DWORD WINAPI CThreadStream::LoadingThreadFunc(LPVOID thread_obj)
{
CThreadStream* thread_ptr=(CThreadStream*)(thread_obj);
while (!s_bExitThread)
{
if (WaitForSingleObject(thread_ptr->m_BeginEvent,0)!=WAIT_OBJECT_0)
{
Sleep(0);
continue;
}
thread_ptr->Load(thread_ptr->m_szRequestFilename,
thread_ptr->m_szRequestPathID);
SetEvent(thread_ptr->m_EndEvent); // loading done
ResetEvent(thread_ptr->m_BeginEvent); // reset begin for next loop
}
return 1;
}
這個迴圈每次都檢查開始事件是否被設定,如果沒有,就讓執行緒睡一下下,然後繼續迴圈檢查。用這個方法,我可以讓背景讀檔的執行緒一直活著,有需要的時候,就幫忙讀個檔案。
但是這個執行緒的CPU負擔其實還是很重的,要一直looping檢查。而讀檔的工作,執行的頻率又不是很高。一直looping非常不划算。
所以我將開始事件改成了無限等待,同時加上一個離開執行緒的事件旗標。
DWORD WINAPI CThreadStream::LoadingThreadFunc(LPVOID thread_obj)
{
CThreadStream* thread_ptr=(CThreadStream*)(thread_obj);
HANDLE hWaitHandle[2]={ thread_ptr->m_BeginEvent,thread_ptr->m_QuitEvent };
while (TRUE)
{
DWORD signal=WaitForMultipleObjects(2,hWaitHandle,FALSE,INFINITE); // wait for begin or quit
if (signal==WAIT_OBJECT_0+1) break;
thread_ptr->Load(thread_ptr->m_szRequestFilename,
thread_ptr->m_szRequestPathID);
SetEvent(thread_ptr->m_EndEvent); // loading done
ResetEvent(thread_ptr->m_BeginEvent); // reset begin for next loop
}
return 1;
}
這樣一來,這個執行緒只會在開始事件被設定時才會醒來工作,做完又會繼續等待。
雖然不算是最佳的設計,倒也符合我們現在的需求了。