トップページ > 過去ログ > 記事閲覧
マルチスレッド動作について
名前:朱音 日時: 2011/07/19 02:47

こんばんわ 過去記事などを参照させていただいた結果、サブスレッドでのDxLib関数の運用は控えるべきと思い、 ファイルからの読み込みは自前でサブスレッドに書いて、メインスレッドでCreateGraphFromMem()を使って読み込もうと思いました ですが、正常に動作するときやアクセス違反になる場合もあって正常に動作せず、 いろいろ調べてみましたが納得できる回答が見つからなかったので質問させていただきます 当方の環境はXP Pro SP3でVC++2010を使用しております DxLibは掲示板にある最新版です PCはデュアルコア環境です Graphクラスを作っており、その中で読み込み自体はstaticな一本のサブスレッドで行っています 同時に複数ロード指示のあった場合に関しては、staticなstd::list<Graph*>に格納し、スレッドが空き次第逐次読み込む方式になっております GetSet系関数は基本的にメンバとstatic関数とのやり取り用となっています GraphListは読み込み済みリストで、裏画面復帰のコールバックで使用します SetMultiThreadFlag()はこの場合必要なのかどうなのかというところですが、入れたり無しにしてみたりしてますが変化無しです サブスレッドでpGraphを使用して参照している変数は、スレッドが実行している間はどこからも使用されていません スレッドがGetExitCodeThread()で終了したのを確認した上で、CloseHandle()も呼んでるのになんでロードできないんだー!って感じです 落ちる箇所はCreateGraphFromMemのところのようです サブスレッド内部のファイル読み込み部をGraphThreadCtrl()内の☆★のところに移動してみると正常に動くようです 描画テストでは、同じ画像を別々のGraph変数で5つほどまとめて定義して、その後定義順にまとめて描画ということをやっています コード内で使用しているLoadOrderListも使用される感じになります あと思ったんですが、スレッド間でデータの書き換えと読み込みが衝突すると破損しますが、読み込み同士が衝突した場合でも破損するのでしょうか? よろしくお願いします>< //staticなロード待ちリスト、先頭要素は現在スレッドで処理中、Iteはイテレータです LoadOrderList; //staticなスレッドハンドル、ロード待ちリストに何もない状態でロード指示があると、待ちリストに組み込みつつその場でスレッドを生成する関数が他にあります ThreadHandle; //サブスレッドの実行結果が返ってきます ThreadBack; void Graph::GraphThreadCtrl(){ //この関数は毎ループ呼ばれます static DWORD ExCode; LoadOrderListIte =LoadOrderList.begin(); if(ThreadHandle != NULL){ if(GetExitCodeThread(ThreadHandle, &ExCode) != STILL_ACTIVE){//スレッドが終了していれば CloseHandle(ThreadHandle);//スレッドを閉じる //☆★☆★☆ //読み込みが成功していれば、ハンドルを生成する if(ThreadBack == STATE_STANDBY)(*LoadOrderListIte)->SetGraphHandle(CreateGraphFromMem((*LoadOrderListIte)->GetBufferPoint(),(*LoadOrderListIte)->GetBufferSize())); (*LoadOrderListIte)->SetState(ThreadBack);//ロード結果を記録 GraphList.push_back(*LoadOrderListIte);//ロード済みデータとして追加する LoadOrderList.erase(LoadOrderListIte);//処理済として削除 LoadOrderListIte =LoadOrderList.begin(); if(!LoadOrderList.empty()){ //待ちリストになにかあれば ThreadHandle = (HANDLE)_beginthreadex(NULL, 0, GraphLoadThread, *LoadOrderListIte, 0, NULL); (*LoadOrderListIte)->SetState(STATE_LOADING);//ロード状態へ } else ThreadHandle = NULL;//待ちリストが空っぽ } } } unsigned __stdcall Graph::GraphLoadThread(void *p){ Graph* pGraph = (Graph*)p;//制御を持ってくる FILE *fp; // ファイルを開く if(fopen_s(&fp, pGraph->GetPath().c_str(), "rb" ) == 0){ // ファイルのサイズを得る fseek( fp, 0L, SEEK_END ); // ファイルの読み込み位置を終端にする pGraph->SetBufferSize(ftell( fp )); // 終端の位置はすなわちファイルのサイズ fseek( fp, 0L, SEEK_SET ); // ファイルの読み込み位置を先頭に戻す // メモリの確保 pGraph->SetBufferPoint(new unsigned char[pGraph->GetBufferSize()]); // ファイルの読み込み fread( pGraph->GetBufferPoint(), pGraph->GetBufferSize(), 1, fp ); // ファイルを閉じる fclose( fp ); ThreadBack = STATE_STANDBY; } else ThreadBack = STATE_NON; _endthreadex(0); return 0; } コールスタックも書いておきます 黄→Game.exe!DxLib::SubHandleList() + 0x10e バイト C++ Game.exe!DxLib::jpeg_general_src() + 0x139 バイト Game.exe!_jinit_marker_reader() + 0x2108 バイト Game.exe!_jinit_marker_reader() + 0x771 バイト Game.exe!_jinit_input_controller() + 0x415 バイト Game.exe!_jpeg_consume_input() + 0x48 バイト Game.exe!_jpeg_read_header() + 0x35 バイト Game.exe!DxLib::LoadJpegImage() + 0x9a バイト Game.exe!DxLib::CreateGraphImageType2() + 0xa0 バイト C++ Game.exe!DxLib::CreateGraphImageOrDIBGraph() + 0xcf バイト C++ Game.exe!DxLib::CreateGraphImage_plus_Alpha() + 0x3a バイト C++ Game.exe!DxLib::CreateGraphFromMem() + 0x40 バイト C++ 緑→Game.exe!Graph::GraphThreadCtrl() 行 105 + 0x3a バイト C++ Game.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) 行 39 + 0x18 バイト C++ Game.exe!__tmainCRTStartup() 行 275 + 0x2c バイト C Game.exe!WinMainCRTStartup() 行 189 C kernel32.dll!7c817077() [下のフレームは間違っているか、または見つかりません。kernel32.dll に対して読み込まれたシンボルはありません。]

Page: 1 |

Re: マルチスレッド動作について ( No.1 )
名前:いっち 日時:2011/07/19 18:19

再現可能なコードをご提供頂けますか? (分量が多いようでしたらアップローダーでお願いします)
Re: マルチスレッド動作について ( No.2 )
名前:クラスクラス 日時:2011/07/19 19:18

書かれた情報から正確なところは読み取れませんが。 3度目の修正^^; う〜〜ん、ソースに見えない部分がどうなっているのか分からないから何とも… 情報が足りないように感じました。 (だんだん意味がないレスに、4回目に修正したら、レスその物が消えそうだ><) いっちさんと同じく、問題の再現ソースがあるといいですね。
Re: マルチスレッド動作について ( No.3 )
名前:クラスクラス 日時:2011/07/19 19:39

提案なので別レスにしました。 ファイルの読込みを並列に動作したいのであれば。 非同期ファイル・アクセスを使用してはいかがですか? 参考ページ。 Win32API編 第52章 非同期的なファイルの読み書き   ↓ >//w○w.geocities.jp/ky_webid/win32c/052.html
Re: マルチスレッド動作について ( No.4 )
名前:朱音(解決) 日時:2011/07/19 20:10

ありがとうございます >>クラスクラスさん 修正前の書き込みを拝見させていただいたので、それを参考にほぼ正常起動するようにすることができました ただ、タイミングによってはうまく行かないだろうと思われるパターンがあとひとつ把握しているので、 それの調整を行っておりました 非同期読み込みですか! これは調べてみたことがなかったです 参考にしてみます とりあえず大本の原因が把握できましたので、解決とさせていただきます ありがとうございました! ちなみにLoadOrderListIteは static std::list<Graph*>::iterator LoadOrderListIte; となっているので大丈夫なのです^^
Re: マルチスレッド動作について ( No.5 )
名前:いっち(解決) 日時:2011/07/19 21:41

解決なさったとの事で何よりなのですが、できれば原因と解決策を記載して頂けますか? で、記載して頂いたコードで以下の行が気になりました。 > if(GetExitCodeThread(ThreadHandle, &ExCode) != STILL_ACTIVE){//スレッドが終了していれば GetExitCodeThread の戻り値は BOOL だと思うのですが、この比較は有効なのでしょうか? それと、わたしなりに再現コードを書いてみたのですが、私の環境では再現しませんでした。 ※ テストコードを若干修正しました。 //- 以下、テストコード -// // だいぶ形は違いますが、やろうとしていることは一緒だと思います。 #include <process.h> #include "DxLib.h" struct GraphicImage { const char* filename; bool load_done; bool create_done; void* data; size_t data_size; int handle; }; GraphicImage graphic_image_list[] = { { "test.bmp", false, false, 0, 0, 0 }, { "test.jpg", false, false, 0, 0, 0 }, { "test.png", false, false, 0, 0, 0 }, // 以下適当にデータを追加してください。私は1000個まで確認しました。 }; const size_t graphic_image_list_max = sizeof(graphic_image_list) / sizeof(*graphic_image_list); int loading_progress = 0; bool quit_thread = false; unsigned int __stdcall LoadGraphicFileThread( void *p ) { for ( size_t i = 0; i < graphic_image_list_max; i++, loading_progress = i ) { FILE* fp = fopen( graphic_image_list[i].filename, "rb" ); fseek( fp, 0, SEEK_END ); graphic_image_list[i].data_size = ftell( fp ); fseek( fp, 0L, SEEK_SET ); graphic_image_list[i].data = new unsigned char[graphic_image_list[i].data_size]; fread( graphic_image_list[i].data, graphic_image_list[i].data_size, 1, fp ); graphic_image_list[i].load_done = true; fclose( fp ); if ( quit_thread ) break; Sleep( 0 ); } _endthreadex( 0 ); return 0; } int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) { ChangeWindowMode( TRUE ); SetWindowText( "DxLib:" DXLIB_VERSION_STR ); if ( DxLib_Init( ) == -1 ) return -1; int white = GetColor( 255, 255, 255 ); unsigned int thread_id; HANDLE thread_handle; thread_handle = (HANDLE)_beginthreadex( NULL, 0, LoadGraphicFileThread, NULL, 0, &thread_id ); size_t loaded_graphic_counter = 0; SetDrawScreen( DX_SCREEN_BACK ); while ( ProcessMessage( ) == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 ) { if ( loaded_graphic_counter >= graphic_image_list_max ) break; if ( graphic_image_list[loaded_graphic_counter].load_done ) { graphic_image_list[loaded_graphic_counter].handle = CreateGraphFromMem( graphic_image_list[loaded_graphic_counter].data, graphic_image_list[loaded_graphic_counter].data_size ); graphic_image_list[loaded_graphic_counter].create_done = true; DeleteGraph( graphic_image_list[loaded_graphic_counter].handle ); // 読み込みすぎ対策 loaded_graphic_counter++; } ClearDrawScreen( ); DrawFormatString( 0, 0, white, "TEST %d/%d/%d", loaded_graphic_counter, loading_progress, graphic_image_list_max ); ScreenFlip( ); } WaitKey( ); quit_thread = true; WaitForSingleObject( thread_handle, INFINITE ); CloseHandle( thread_handle ); DxLib_End( ); for ( size_t i = 0; i < graphic_image_list_max; i++ ) { delete [] graphic_image_list[i].data; } return 0; }
Re: マルチスレッド動作について ( No.6 )
名前:朱音(解決) 日時:2011/07/19 21:59

まさしくそれでした、w わざわざサンプルコードまで用意していただき恐縮です・・・ 確かどこかのサイトを参考にそのまま書いたはずなのですが、 先ほどMSDNを見て愕然といたしました・・・いや恥ずかしいorz そのほかにもなるべく共有で参照する変数を減らすようにしました マルチスレッドプログラムを書いてみたのはこれがはじめてでしたので色々手探り状態でしたが、 お二方のアドバイスのおかげで色々なミスに気がつき、大変勉強になりました これからも何かお聞きすることがあるかもしれませんが、そのときはよろしくお願いいたします>< ありがとうございました!(´▽`)
Re: マルチスレッド動作について ( No.7 )
名前:いっち(解決) 日時:2011/07/19 23:00

余談ですが、今回のようなファイルの読み込み処理を並列化しない場合であれば、 DXライブラリのFileRead_系を使っても問題ないと思います。 DXアーカイブを利用した場合は、単にファイルをメモリに置くだけであれば、 DXArchivePreLoad を利用できます。(並列でハンドル化とかはできません) ※ DXArchivePreLoad について誤解を与えそうでしたので記述を変えました。

Page: 1 |