プログラミングメモ →目次

メモリリーク問題

並列パターン ライブラリ (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;
}