メモリリーク問題 並列パターン ライブラリ (PPL) MFCのディバッグ環境で、並列パターン ライブラリ (PPL)の利用すると、プログラム終了した時に、多量のメモリリークが報告されてしまいます。MFCのアプリケーションでは、_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF)により、メモリのリークしているかどうかをチェックしています。例えば、下記のConsoleプログラムでも、実行後、メモリリークが報告されます。 #include "stdafx.h" #include <Windows.h> #include <iostream> #include <sstream> #include <ppl.h> using namespace std; using namespace Concurrency; int _tmain(int argc, _TCHAR* argv[]) { int iDbgFlag; iDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); iDbgFlag |= _CRTDBG_LEAK_CHECK_DF; _CrtSetDbgFlag(iDbgFlag); parallel_for(0, 10, [](int i) { stringstream txt; txt << i << endl; cout << txt.str(); }); return 0; } 普段には、PPL 及び エージェント ライブラリ のオブジェクトが生成される時に、既定のタスク・スケジューラが必要です。タスク・スケジューラも、一つのリソース・マネージャが必要です。もし、PPLのスレッド、或いは、エージェントには、関連のスケジューラが無ければ、一つのデフォルトのタスク・スケジューラが作成されます。タスク・スケジューラの寿命は、関連するスレッドに依存されます。スレッドが終了されると、タスク・スケジューラが消滅されます。このデフォルトのタスク・スケジューラがメインスレッドに関連されるので、メインスレッドが終了された時に、消滅されます。実は、メモリリークは発生していません。原因しては、メモリリークのチェック処理が、メモリの開放のタイミングより早く行われている分けです。これを証明するために、専用のタスク・スケジューラを作ってから、PPLのコード実行させます。PPLのコードの実行が終わったら、タスク・スケジューラを消滅します。するとメモリリークが報告されなくなります。プログラムは下記のようです。専用のタスク・スケジューラが完全に消滅され、メモリが開放する為に、500msぐらいの遅延を入れています。 #include "stdafx.h" #include <Windows.h> #include <iostream> #include <sstream> #include <ppl.h> using namespace std; using namespace Concurrency; int _tmain(int argc, _TCHAR* argv[]) { HANDLE hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); CurrentScheduler::Create( SchedulerPolicy() ); CurrentScheduler::RegisterShutdownEvent( hEvent ); int iDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); iDbgFlag |= _CRTDBG_LEAK_CHECK_DF; _CrtSetDbgFlag(iDbgFlag); parallel_for(0, 10, [](int i) { stringstream txt; txt << i << endl; cout << txt.str(); }); CurrentScheduler::Detach(); WaitForSingleObject( hEvent, INFINITE ); CloseHandle( hEvent ); Sleep(500); return 0; } |