3 に答える
threadprivate
MS OpenMP 実装__declspec(thread)
では、宣言された変数を静的 TLS (Thread-Local Storage) に配置するように変換されます。プログラムが開始されると、TLS のサイズは、実行可能ファイルに必要な TLS サイズと、暗黙的にロードされた他のすべての DLL の TLS 要件を考慮して決定されます。を使用して別の DLL を動的にLoadLibrary
ロードまたはアンロードするFreeLibrary
場合、システムは実行中のすべてのスレッドを調べ、それに応じて TLS ストレージを拡大または圧縮する必要があります。KB118816によると:
このプロセスは、オペレーティング システムが管理するには多すぎるため、DLL が動的に読み込まれるか、コードがデータを参照するときに、例外が発生する可能性があります。
このような変数へのアクセスは、未定義の動作と見なされます。あなたの場合は機能しますが、いつでもどこでも機能するとは限りません。ここでは、Windows XP/2003 およびそれ以前の Windows バージョンで失敗する可能性が最も高い理由を正確に読み取ることができます。同じソースによると、暗黙の TLS 処理は Windows Vista で書き直されたため、OpenMPはthreadprivate
それ__declspec(thread)
以降、実行時に読み込まれる DLL で正しく機能するはずです。提案された解決策は、TlsAlloc
代わりに使用することです。
DWORD dwTlsIdx;
extern "C" __declspec(dllexport) int MyFunc1(void)
{
int i;
#pragma omp parallel for
for (i = 0; i < n; i++) {
MyFunc2(i);
}
return TRUE;
}
void MyFunc2(void)
{
int **pp = (int **)TlsGetValue(dwTlsIdx);
*pp = malloc(sizeof(int));
**pp = 0;
printf(“value = %d”,**pp);
free(*pp);
}
dwTlsIdx
DllMain
への呼び出しを使用して、プロセスのアタッチ時に初期化する必要がありますTlsAlloc
。保持するのに十分なメモリをスレッドのアタッチ時に割り当て、そのアドレスをTLS インデックスint *
の値として設定する必要があります。dwTlsIdx
または、代わりに最初の呼び出しでそれを行うことができますMyFunc2
:
void MyFunc2(void)
{
int **pp = (int **)TlsGetValue(dwTlsIdx);
if (pp == NULL)
{
pp = malloc(sizeof(int *));
TlsSetValue(dwTlsIdx, pp);
}
*pp = malloc(sizeof(int));
**pp = 0;
printf(“value = %d”,**pp);
free(*pp);
}
詳しくはこちらをご覧ください(エラーチェックあり)。
私は OMP を試していませんでしたが、OMP の使用経験は豊富です。
DWORD WINAPI TlsAlloc(void);
およびその他のTlsXxx
機能。threadprivate(p)
最終的にこの関数に到達するラッパーであると確信しています。この関数は、exe、静的および動的にロードされた dll などで完全に機能します。
あるケースでは、私のプロセスで同時に約 800 の TLS インデックスを見ていました。各スレッド (約 200 スレッド) は、スレッド ローカル ストレージにこの数のオブジェクトを保持していました。NT は、このデータを格納するためにヒープにバッファを割り当てていました。そして、これはすべてうまくいきました。
MSDN の記事が書かれた時点で何らかの問題があった可能性がありますが、現在は修正されている可能性が高いです。
私の2セント。
フリストとキリル、
回答ありがとうございます。
Microsoft は、動的 DLL の TLS に関する問題を修正したようです (少なくとも、私が使用している Windows 7 では)。おそらく、彼らはドキュメントを更新しなかっただけです。私は指を交差させ続けます。
__declspec(thread) プラグマを介して TLS を使用してアイデアをテストするテスト プロジェクトを作成しました (Hristo Iliev が他の投稿で提案したように)。このプロジェクトでは、多数の TLS 変数を (テスト目的で) 使用しましたが、すべて正常に機能しました。次に、コードを作業プロジェクトに移動しました。これは、多数の動的 DLL をロードする大規模なプロジェクト (約 100 万行のコード) です。それはすべてそこでも機能しました。
最悪のシナリオでは、TlsAlloc、TlsSetValue、TlsGetValue 関数を使用するというアイデアをまだ保持しています。しかし、これらの関数が私が持っている数値計算コードにかなりのオーバーヘッドを追加するのではないかと心配しているので、私にはうまく合いません。私のライブラリにはいくつかの TLS 変数しか必要ありませんが、これらの変数はコード全体で広く使用されており、スタックの奥深くにある低レベル関数も含まれています。
乾杯、
アレックス・ヤムチコフ