写真の表示中に、裏で次の写真ファイルを読み込む処理をする。
Threadの開始は
HANDLE LoaderHandle = CreateThread(NULL,0,LoaderThreead,0,0,(LPDWORD)&ThreadID);
最初の NULL はセキュリティ属性、指定しないのでNULL
次はThreadのスタックサイズ、指定しないので0
3番目は実行する関数名、LoaderThreead
4番目は実行関数に渡すパラメータ、指定しないので0
5番目はThreadをすぐ実行するか?サスペンドした状態のThreadを作るか?を指定する。すぐに実行する場合は0を指定、CREATE_SUSPENDEDで停止状態のThreadが作られる
停止中のThreadはResumeThreadで再開できる。実行中のThreadをSuspendThreadで停止することも出来る
LoaderThreead関数が終了するとThreadも終了するが、CloseHandle()をしないとThreadのオブジェクトが残ってメモリリークとなる
マルチスレッド化したのでレンダリング処理(RenderThread)と写真のロード処理(LoaderThread)が独立して動くようになるので、2つのthread間で協調して動かせるようにEventを使った
具体的には、
- RenderThreadは次の写真が必要になるとLoaderThreadが写真のロード処理が完了するまでThreadを停止して待つ
- LoaderThreadは写真のロードが完了したことをRenderThreadに伝え、さらに次の写真を読み込む要求がRenderThreadから来るまでThreadを停止して待つ
- RenderThreadは写真の表示の合間に次の写真をロードするようにLoaderThreadに要求を出す
といった具合にお互いの状態を連絡しあって処理をする事を同期処理というらしい
この同期にEventという仕組みがOSに用意されている。Eventの説明は検索するといっぱいヒットするので省略
Eventを使うメリットは処理待ちの間Threadが停止するので、ポーリングするより余計なCPUパワーを消費しないで済むことだと思う
Eventを使用するにはCreateEventでイベントを作成してHANDLEを取得する
m_LoadRequest = CreateEvent(NULL, FALSE, FALSE, TEXT("LOADREQUEST"));
Threadと同様にハンドルをクローズする必要がある
CloseHandle(m_LoadRequest);
Eventを待つときは
WaitForSingleObject(m_LoadRequest,INFINITE);
Eventをセットするときは
SetEvent(m_LoadRequest);
今回はイベントを自動リセットとしたのでResetEventを呼ぶ必要は無い
ThreadとEvent関連のソース抜粋
bool m_threadEnd;
HANDLE m_RenderHandle;
HANDLE m_LoaderHandle;
HANDLE m_UpdateEvent;
HANDLE m_LoadCmplete;
HANDLE m_LoadRequest;
void OnCreate(HWND hWnd)
{
WORD ThreadID;
gFrame = 0;
gNextFrame = 0;
pictnow = NULL;
pictload = NULL;
m_LoadRequest = CreateEvent(NULL, FALSE, FALSE, TEXT("LOADREQUEST")); // 自動リセット reset状態
m_LoadCmplete = CreateEvent(NULL, FALSE, FALSE, TEXT("LOADEREVENT")); // 自動リセット reset状態
m_UpdateEvent = CreateEvent(NULL, FALSE, TRUE , TEXT("UPDATEEVENT")); // 自動リセット
m_LoaderHandle = CreateThread(NULL,0,LoaderThreead,0,0,(LPDWORD)&ThreadID);
m_RenderHandle = CreateThread(NULL,0,RenderThreead,0,0,(LPDWORD)&ThreadID);
SetThreadPriority(m_RenderHandle,THREAD_PRIORITY_TIME_CRITICAL);
}
void OnDestroy(HWND hWnd)
{
m_threadEnd = TRUE;
// 停止中のスレッドに終了を伝える為にeventをセットする
SetEvent(m_UpdateEvent);
SetEvent(m_LoadRequest);
// ローダースレッドが処理を完了するまで待つ
WaitForSingleObject(m_LoadCmplete,INFINITE);
CloseHandle(m_LoadCmplete);
CloseHandle(m_LoaderHandle);
CloseHandle(m_RenderHandle);
CloseHandle(m_UpdateEvent);
CloseHandle(m_LoadRequest);
}
DWORD WINAPI RenderThreead(LPVOID lpParameter)
{
int frame = -1;
int render_frame = -1;
while(m_threadEnd!=TRUE)
{
// 描画するフレームを決定
if(frame<gFrame)
{
// フレーム更新
frame = gFrame;
}
else
{
// 更新前だが次のフレームを用意しておく
frame = gFrame +1;
}
if(frame>render_frame)
{
render_frame = frame;
if((pictnow==NULL)||(pictnow->IsEnd(frame)))
{
SetThreadPriority(m_LoaderHandle,THREAD_PRIORITY_ABOVE_NORMAL);
WaitForSingleObject(m_LoadCmplete,INFINITE);
if(pictnow)
delete pictnow;
if(pictload==NULL)
break;
pictnow = pictload;
pictload = NULL;
// ローダーを再開する(再開前にスレッドの優先度を下げておく)
SetThreadPriority(m_LoaderHandle,THREAD_PRIORITY_IDLE);
SetEvent(m_LoadRequest);
}
pictnow->Draw(pScreen->m_Buf,pScreen->m_dspW,pScreen->m_dspH,render_frame);
}
else
{
WaitForSingleObject(m_UpdateEvent,INFINITE);
}
}
return 0;
}
DWORD WINAPI LoaderThreead(LPVOID lpParameter)
{
while(m_threadEnd!=TRUE)
{
// 写真のファイル名を取得
TCHAR *fname = FindFile();
if(fname ==NULL)
{
// 取得できなかった
// SendMessage();
pictload = NULL;
break;
}
// ファイルを読み込む
pictload = new CPicture();
if(!pictload->LoadPicture(fname,gNextFrame))
{
// ファイルが読めなかった
continue;
}
// 次のファイルの開始フレームを決定
gNextFrame = pictload->frame_end;
// RenderThread にロード完了したことを伝える
SetEvent(m_LoadCmplete);
// さらに次のファイルを読み込みを開始できるまで待つ
WaitForSingleObject(m_LoadRequest,INFINITE);
}
// スレッド終了待ちを解除するため
SetEvent(m_LoadCmplete);
return 0;
}
0 件のコメント:
コメントを投稿