EDITです:
TL。ドクター
それは動作します。すぐに使える解決策はこの答えの最後です。
/EDIT
私の質問にタイミングサンプルを追加している間、私は解決策のためのアイデアを持っていた:
私の目標は、それらのすべてが(以下実装)自由である場合にのみ、ロックを取得することで、ロックを保持し、タイムアウト時に受信したロックのみを返すように簡単に変更できます。
この部分は、ロックするオブジェクト(またはmutex)の配列とタイムアウトを受け取る静的ヘルパー関数にさらに移動できます。
編集:
完了、答えの最後を参照してください。
//-------------------------------------
// using direct implementation
//-------------------------------------
void UseAllResources2()
{
bool bSuccess1 = false;
bool bSuccess2 = false;
try
{
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() start locking 1 and 2");
DateTime tStart = DateTime::Now;
bool bSuccess = false;
do
{
bSuccess1 = Monitor::TryEnter (oResource1);
bSuccess2 = Monitor::TryEnter (oResource2);
bSuccess = bSuccess1 && bSuccess2;
if (!bSuccess)
{
if (bSuccess1) Monitor::Exit (oResource1);
if (bSuccess2) Monitor::Exit (oResource2);
Thread::Sleep(10);
}
}
while (!bSuccess && (DateTime::Now - tStart).TotalMilliseconds < msc_iTimeoutMonitor);
if (!bSuccess)
{
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() monitor timeout");
return;
}
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() start work");
Thread::Sleep (400);
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() finish work");
} finally {
if (bSuccess2)
Monitor::Exit (oResource2);
if (bSuccess1)
Monitor::Exit (oResource1);
}
}
//-------------------------------------
// using Out-Of-Box solution
//-------------------------------------
static void UseAllResources3()
{
bool bSuccess = false;
try
{
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() start locking 1 and 2");
bSuccess = MonitorTryEnter (gcnew array<Object^>{oResource1, oResource2}, 500, 10, false);
if (!bSuccess)
{
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() monitor timeout");
return;
}
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() start work");
Thread::Sleep (400);
Console::WriteLine (DateTime::Now.ToString("ss.fff") + " "__FUNCTION__"() finish work");
} finally {
if (bSuccess)
{
Monitor::Exit (oResource2);
Monitor::Exit (oResource1);
}
}
}
私のテストのための()メイン:
int main()
{
// first run is for the CLR to load everything
Thread^ oThreadA = gcnew Thread (gcnew ThreadStart (&UseResource1));
Thread^ oThreadB = gcnew Thread (gcnew ThreadStart (&UseResource2));
// Thread^ oThreadZ = gcnew Thread (gcnew ThreadStart (&UseAllResources));
// Thread^ oThreadZ = gcnew Thread (gcnew ThreadStart (&UseAllResources2));
Thread^ oThreadZ = gcnew Thread (gcnew ThreadStart (&UseAllResources3));
oThreadB->Start();
Thread::Sleep(100);
oThreadZ->Start();
Thread::Sleep(100);
oThreadA->Start();
Thread::Sleep (2000);
Console::WriteLine();
// now that all code is JIT compiled, the timestamps are correct.
// Logs below are from this 2nd run.
oThreadA = gcnew Thread (gcnew ThreadStart (&UseResource1));
oThreadB = gcnew Thread (gcnew ThreadStart (&UseResource2));
// oThreadZ = gcnew Thread (gcnew ThreadStart (&UseAllResources));
// oThreadZ = gcnew Thread (gcnew ThreadStart (&UseAllResources2));
oThreadZ = gcnew Thread (gcnew ThreadStart (&UseAllResources3));
oThreadB->Start();
Thread::Sleep(100);
oThreadZ->Start();
Thread::Sleep(100);
oThreadA->Start();
Thread::Sleep (2000);
}
出力UseAllResources():開始)((質問から)
01.503 UseResource2()開始
01.503 UseResource2をロックする作業
01.604 UseAllResources()ロック開始1
01.604 UseAllResources()ロック開始2
01.707 UseResource1())仕上げ作業
出力UseAllResources2()(タイムアウト
02.303 UseAllResourcesを監視する)作業
02.211 UseResource1(開始)(
01.903 UseResource2()仕上げ作業
01.903 UseAllResourcesロック開始: (第1溶液、直接実装)
42.002 UseResource2は())(ワーク
42.103 UseAllResources2開始)(
42.002 UseResource2ロック起動1及び2ロック開始します 42.206 UseResource1()作業開始)(
42.206 UseResource1ロック開始
42.256 UseResource1()仕上げ作業
42.402 UseResource2()仕上げ作業
42.427 UseAllResources2()開始ワーク
42.827 UseAllResources2()仕上げ作業
出力UseAllResources3(keepLocks =偽):(2溶液、アウトオブボックス実装)
16.392 UseResource2()
16.393 UseResouロック開始rce2()は作業開始)(
16.597 UseResource1ロック開始)()(ワーク
16.494 UseAllResources3起動1及び2
16.595 UseResource1ロック開始
16.647 UseResource1()仕上げ作業
16.793 UseResource2()仕上げ作業
16.818 UseAllResources3()start work
17.218 UseAllResources3()は作業を完了します
//これは以前と同じです。
出力UseAllResources3(keepLocks =真):(2溶液、アウトオブボックス実装)
31.965 UseResource2()
31.965 UseResource2を(ロック開始)
32.068作業を開始UseAllResources3(1)をロック開始および2
32.169 UseResource1()ロックの開始
32。スレッドAに365 UseResource2()仕上げ作業
32.390 UseAllResources3()開始ワーク
32.672 UseResource1()監視タイムアウト
32.790 UseAllResources3()仕上げ作業
//タイムアウト、予想通り。
WORKS! :-)
TL; DR
はここでアウトオブボックスソリューションです:あなたがロックを同期させるための別の外側のロックを導入しない限り、あなたは(すべて一緒に複数のロックを取得することはできません
//----------------------------------------------------------------------------
// MonitorTryEnter
//----------------------------------------------------------------------------
bool MonitorTryEnter (array<Object^>^ i_aoObject, int i_iTimeout, int i_iSleep, bool i_bKeepLocks)
{
if (!i_aoObject)
return false;
if (i_iSleep < 0)
i_iSleep = 10;
List<Object^>^ listObject = gcnew List<Object^>;
for (int ixCnt = 0; ixCnt < i_aoObject->Length; ixCnt++)
if (i_aoObject[ixCnt])
listObject->Add (i_aoObject[ixCnt]);
if (listObject->Count <= 0)
return false;
array<bool>^ abSuccess = gcnew array<bool>(listObject->Count);
DateTime tStart = DateTime::Now;
bool bSuccess = true;
do
{
bSuccess = true;
if (!i_bKeepLocks)
abSuccess = gcnew array<bool>(listObject->Count);
for (int ixCnt = 0; ixCnt < listObject->Count; ixCnt++)
{
if (!abSuccess[ixCnt])
abSuccess[ixCnt] = Monitor::TryEnter (listObject[ixCnt]);
bSuccess = bSuccess && abSuccess[ixCnt];
if (!bSuccess)
break;
}
if (!bSuccess)
{
if (!i_bKeepLocks)
{
for (int ixCnt = 0; ixCnt < listObject->Count; ixCnt++)
{
if (abSuccess[ixCnt])
{
Monitor::Exit (listObject[ixCnt]);
abSuccess[ixCnt] = false;
}
}
}
Thread::Sleep(i_iSleep);
}
}
while (!bSuccess && (DateTime::Now - tStart).TotalMilliseconds < i_iTimeout);
if (!bSuccess)
{
for (int ixCnt = 0; ixCnt < listObject->Count; ixCnt++)
if (abSuccess[ixCnt])
Monitor::Exit (listObject[ixCnt]);
}
return bSuccess;
}
買収...悪い、IMO)。しかし、複数の取得されたロックは頭痛や白いデバッグの夜(良いデッドロックのため)の良い出発点です、あなたは一度に1つのリソースしか取得して使用できませんか?あなたは_long syntax_の代わりに 'msclr :: lock'を使いたいかもしれません。もしリソースが関連していれば、2つのファインドロックで遊ぶよりも粗いロックを使うほうがいいです**(そして、 'ReaderWriterLockSlim'のパフォーマンスを調べる) –
@AdrianoRepetti:データをシフトするので、いくつかの機能では、あるリソースから別のリソースへタイムアウトを使用することでデッドロックが発生しないようにする必要があります。 –
trueですが、ロックを取得できない場合は、必要な操作を実行せずに戻ります。それは実際にプロダクションで何をしたいのですか?タイムアウトがうまくいかないので、あなたはこの質問をしています。ポイントは...同時ロックを取得せず、粗い(共有)ロックに移動しないことです。ロック戦略は(IMO)複雑すぎて架空の例では設計できませんが、あなたの読み込みパターンは何ですか?パターンを書く?並行読み取りと書き込み?より粗いロックを使用すると、パフォーマンスは低下しますか?細かいロックのメリットは何ですか?あなたがロック取得ロックを導入する必要があるのであれば、BTW ... –