同时获取多个线程同步锁
acquire multiple thread sync locks simultaneously
假设以下代码在我的应用程序中以类似的方式使用:
//-------------------------------------
void UseAllResources ()
{
bool bSuccess1 = false;
bool bSuccess2 = false;
try
{
bSuccess1 = Monitor::TryEnter (oResource1, msc_iTimeoutMonitor);
if (!bSuccess1) return;
bSuccess2 = Monitor::TryEnter (oResource2, msc_iTimeoutMonitor);
if (!bSuccess2) return;
// work on oResource1 and oResource2
} finally {
if (bSuccess2)
Monitor::Exit (oResource2);
if (bSuccess1)
Monitor::Exit (oResource1);
}
}
//-------------------------------------
void UseResource1 ()
{
bool bSuccess = false;
try {
bSuccess = Monitor::TryEnter (oResource1, msc_iTimeoutMonitor);
if (!bSuccess) return;
// work on oResource1
} finally {
if (bSuccess) Monitor::Exit (oResource1);
}
}
//-------------------------------------
void UseResource2 ()
{
same like UseResource1(), but using oResource2
}
这些函数可能随时被不同的线程调用。
有可能
(超时为 500 毫秒)
@t=0ms,线程 B 正在执行 UseResource2()
,将花费 400ms,
@t=100ms,线程 Z 正在调用 UseAllResources(),获取了 oResource1 上的锁,必须等待 oResource2 上的锁,
@t=200ms,线程 A 正在调用 UseResource1() 并且必须等待 oResource1 上的锁,它被线程 Z 占用,
@t=400ms,线程 B 完成,线程 Z 锁定 oResource2
并开始工作,需要 400ms,
@t=700ms,线程 A 超时,尽管它只需要 50 毫秒并且可以在线程 Z 仍在等待时工作。
我宁愿线程 Z 失败,如果有的话,因为超时应该是所有锁的总值。
我可以同时开始获取多个锁吗?
一个解决方案可能是使用 ReaderWriterLockSlim class。下面的代码在构造函数中包装了一个函数(你要做的工作)。或者,您可以将该函数移动到 DoWork 方法以更改您访问资源的方式。
LockedResource 实现
class LockedResource
{
public delegate void RefAction();
ReaderWriterLockSlim resourceLock;
public LockedResource()
{
//Warning: SupportsRecursion is risky, you should remove support for recursive whenever possible
resourceLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
}
public bool DoWork(RefAction work, string threadname, int timeout = -1)
{
try
{
if (resourceLock.TryEnterWriteLock(timeout))
{
if (work != null)
{
work();
}
}
else
{
Console.WriteLine("Lock time out on thread {0}", threadname);
}
}
finally
{
Console.WriteLine("{0} releasing resource", threadname);
if(resourceLock.IsWriteLockHeld)
{
resourceLock.ExitWriteLock();
}
}
return false;
}
}
示例用法
static void Main(string[] args)
{
object oResouce1 = "-";
object oResouce2 = "-";
LockedResource lock1 = new LockedResource();
LockedResource lock2 = new LockedResource();
//the event wait handles is not required, only used to block thread so that resource values can be printed out at the end of the program
var h1 = new EventWaitHandle(false, EventResetMode.ManualReset);
var h2 = new EventWaitHandle(false, EventResetMode.ManualReset);
var h3 = new EventWaitHandle(false, EventResetMode.ManualReset);
WaitHandle[] waitHandles = { h1, h2, h3 };
var t1 = new Thread(() =>
{
lock1.DoWork(() =>
{
oResouce1 = "1";
Console.WriteLine("Resource 1 set to 1");
},"T1");
h1.Set();
});
var t2 = new Thread(() =>
{
lock2.DoWork(() =>
{
oResouce2 = "2";
Console.WriteLine("Resource 2 set to 2");
Thread.Sleep(10000);
}, "T2");
h2.Set();
});
var t3 = new Thread(() =>
{
lock1.DoWork(() =>
{
lock2.DoWork(() =>
{
oResouce1 = "3";
Console.WriteLine("Resource 1 set to 3");
oResouce2 = "3";
Console.WriteLine("Resource 2 set to 3");
}, "T3", 1000);
h3.Set();
}, "T3");
});
t1.Start();
t2.Start();
t3.Start();
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Resource 1 is {0}", oResouce1);
Console.WriteLine("Resource 2 is {0}", oResouce2);
Console.ReadLine();
}
输出
Resource 1 set to 1
Resource 2 set to 2
T1 releasing resource
Lock time out on thread T3
T3 releasing resource
T3 releasing resource
T2 releasing resource
Resource 1 is 1
Resource 2 is 2
编辑:
tl;dr
有用。开箱即用的解决方案在此答案的末尾。
/编辑
在为我的问题添加计时示例时,我想到了一个解决方案:
我的目标是仅在所有锁都空闲时才获取锁(在下面实现),但它可以很容易地更改为保持锁并仅在超时时返回接收到的锁。
这部分可以进一步移动到静态辅助函数,该函数接收要锁定的对象(或互斥量)数组和超时。
编辑:
完成,见答案结尾。
//-------------------------------------
// 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);
}
}
}
我的 main() 测试:
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() 开始锁定
01.903 UseResource2() 完成工作
01.903 UseAllResources() 开始工作
02.211 UseResource1() 监视器超时
02.303 UseAllResources() 完成工作
output UseAllResources2():(第一种方案,直接实现)
42.002 UseResource2() 开始锁定
42.002 UseResource2() 开始工作
42.103 UseAllResources2() 开始锁定 1 和 2
42.206 UseResource1() 开始锁定
42.206 UseResource1() 开始工作
42.256 UseResource1() 完成工作
42.402 UseResource2() 完成工作
42.427 UseAllResources2() 开始工作
42.827 UseAllResources2() 完成工作
output UseAllResources3(keepLocks=false):(第二个解决方案,开箱即用的实现)
16.392 UseResource2() 开始锁定
16.393 UseResource2() 开始工作
16.494 UseAllResources3() 开始锁定 1 和 2
16.595 UseResource1() 开始锁定
16.597 UseResource1() 开始工作
16.647 UseResource1() 完成工作
16.793 UseResource2() 完成工作
16.818 UseAllResources3() 开始工作
17.218 UseAllResources3() 完成工作
// 与之前相同,符合预期。
输出 UseAllResources3(keepLocks=true):(第二个解决方案,开箱即用的实现)
31.965 UseResource2() 开始锁定
31.965 UseResource2() 开始工作
32.068 UseAllResources3() 开始锁定 1 和 2
32.169 UseResource1() 开始锁定
32.365 UseResource2() 完成工作
32.390 UseAllResources3() 开始工作
32.672 UseResource1() 监视器超时
32.790 UseAllResources3() 完成工作
// 正如预期的那样,线程 A 超时。
成功了! :-)
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;
}
假设以下代码在我的应用程序中以类似的方式使用:
//-------------------------------------
void UseAllResources ()
{
bool bSuccess1 = false;
bool bSuccess2 = false;
try
{
bSuccess1 = Monitor::TryEnter (oResource1, msc_iTimeoutMonitor);
if (!bSuccess1) return;
bSuccess2 = Monitor::TryEnter (oResource2, msc_iTimeoutMonitor);
if (!bSuccess2) return;
// work on oResource1 and oResource2
} finally {
if (bSuccess2)
Monitor::Exit (oResource2);
if (bSuccess1)
Monitor::Exit (oResource1);
}
}
//-------------------------------------
void UseResource1 ()
{
bool bSuccess = false;
try {
bSuccess = Monitor::TryEnter (oResource1, msc_iTimeoutMonitor);
if (!bSuccess) return;
// work on oResource1
} finally {
if (bSuccess) Monitor::Exit (oResource1);
}
}
//-------------------------------------
void UseResource2 ()
{
same like UseResource1(), but using oResource2
}
这些函数可能随时被不同的线程调用。
有可能
(超时为 500 毫秒)
@t=0ms,线程 B 正在执行 UseResource2()
,将花费 400ms,
@t=100ms,线程 Z 正在调用 UseAllResources(),获取了 oResource1 上的锁,必须等待 oResource2 上的锁,
@t=200ms,线程 A 正在调用 UseResource1() 并且必须等待 oResource1 上的锁,它被线程 Z 占用,
@t=400ms,线程 B 完成,线程 Z 锁定 oResource2
并开始工作,需要 400ms,
@t=700ms,线程 A 超时,尽管它只需要 50 毫秒并且可以在线程 Z 仍在等待时工作。
我宁愿线程 Z 失败,如果有的话,因为超时应该是所有锁的总值。
我可以同时开始获取多个锁吗?
一个解决方案可能是使用 ReaderWriterLockSlim class。下面的代码在构造函数中包装了一个函数(你要做的工作)。或者,您可以将该函数移动到 DoWork 方法以更改您访问资源的方式。
LockedResource 实现
class LockedResource
{
public delegate void RefAction();
ReaderWriterLockSlim resourceLock;
public LockedResource()
{
//Warning: SupportsRecursion is risky, you should remove support for recursive whenever possible
resourceLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
}
public bool DoWork(RefAction work, string threadname, int timeout = -1)
{
try
{
if (resourceLock.TryEnterWriteLock(timeout))
{
if (work != null)
{
work();
}
}
else
{
Console.WriteLine("Lock time out on thread {0}", threadname);
}
}
finally
{
Console.WriteLine("{0} releasing resource", threadname);
if(resourceLock.IsWriteLockHeld)
{
resourceLock.ExitWriteLock();
}
}
return false;
}
}
示例用法
static void Main(string[] args)
{
object oResouce1 = "-";
object oResouce2 = "-";
LockedResource lock1 = new LockedResource();
LockedResource lock2 = new LockedResource();
//the event wait handles is not required, only used to block thread so that resource values can be printed out at the end of the program
var h1 = new EventWaitHandle(false, EventResetMode.ManualReset);
var h2 = new EventWaitHandle(false, EventResetMode.ManualReset);
var h3 = new EventWaitHandle(false, EventResetMode.ManualReset);
WaitHandle[] waitHandles = { h1, h2, h3 };
var t1 = new Thread(() =>
{
lock1.DoWork(() =>
{
oResouce1 = "1";
Console.WriteLine("Resource 1 set to 1");
},"T1");
h1.Set();
});
var t2 = new Thread(() =>
{
lock2.DoWork(() =>
{
oResouce2 = "2";
Console.WriteLine("Resource 2 set to 2");
Thread.Sleep(10000);
}, "T2");
h2.Set();
});
var t3 = new Thread(() =>
{
lock1.DoWork(() =>
{
lock2.DoWork(() =>
{
oResouce1 = "3";
Console.WriteLine("Resource 1 set to 3");
oResouce2 = "3";
Console.WriteLine("Resource 2 set to 3");
}, "T3", 1000);
h3.Set();
}, "T3");
});
t1.Start();
t2.Start();
t3.Start();
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Resource 1 is {0}", oResouce1);
Console.WriteLine("Resource 2 is {0}", oResouce2);
Console.ReadLine();
}
输出
Resource 1 set to 1
Resource 2 set to 2
T1 releasing resource
Lock time out on thread T3
T3 releasing resource
T3 releasing resource
T2 releasing resource
Resource 1 is 1
Resource 2 is 2
编辑:
tl;dr
有用。开箱即用的解决方案在此答案的末尾。
/编辑
在为我的问题添加计时示例时,我想到了一个解决方案:
我的目标是仅在所有锁都空闲时才获取锁(在下面实现),但它可以很容易地更改为保持锁并仅在超时时返回接收到的锁。
这部分可以进一步移动到静态辅助函数,该函数接收要锁定的对象(或互斥量)数组和超时。
编辑:
完成,见答案结尾。
//-------------------------------------
// 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);
}
}
}
我的 main() 测试:
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() 开始锁定
01.903 UseResource2() 完成工作
01.903 UseAllResources() 开始工作
02.211 UseResource1() 监视器超时
02.303 UseAllResources() 完成工作
output UseAllResources2():(第一种方案,直接实现)
42.002 UseResource2() 开始锁定
42.002 UseResource2() 开始工作
42.103 UseAllResources2() 开始锁定 1 和 2
42.206 UseResource1() 开始锁定
42.206 UseResource1() 开始工作
42.256 UseResource1() 完成工作
42.402 UseResource2() 完成工作
42.427 UseAllResources2() 开始工作
42.827 UseAllResources2() 完成工作
output UseAllResources3(keepLocks=false):(第二个解决方案,开箱即用的实现)
16.392 UseResource2() 开始锁定
16.393 UseResource2() 开始工作
16.494 UseAllResources3() 开始锁定 1 和 2
16.595 UseResource1() 开始锁定
16.597 UseResource1() 开始工作
16.647 UseResource1() 完成工作
16.793 UseResource2() 完成工作
16.818 UseAllResources3() 开始工作
17.218 UseAllResources3() 完成工作
// 与之前相同,符合预期。
输出 UseAllResources3(keepLocks=true):(第二个解决方案,开箱即用的实现)
31.965 UseResource2() 开始锁定
31.965 UseResource2() 开始工作
32.068 UseAllResources3() 开始锁定 1 和 2
32.169 UseResource1() 开始锁定
32.365 UseResource2() 完成工作
32.390 UseAllResources3() 开始工作
32.672 UseResource1() 监视器超时
32.790 UseAllResources3() 完成工作
// 正如预期的那样,线程 A 超时。
成功了! :-)
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;
}