问题
I was wondering if a call to Threa.Sleep on a thread that already acquiered a Monitor will release the lock before going to sleep:
object o = new object();
Montior.Enter(o);
Thread.Sleep(1000);
Monitor.Exit(o);
While the thread is suspended - can other thread acquire o
?
回答1:
No, the lock will not be released if you Sleep.
If you want to release it, use Monitor.Wait(o, timeout)
; further, you can also use this to signal from another thread - another thread can use Monitor.Pulse[All]
(while holding the lock) to wake the waiting thread earlier than "timeout" (it will re-acquire the lock in the process, too).
Note that whenever using Enter/Exit, you should consider using try/finally too - or you risk not releasing the lock if an exception happens.
Example:
bool haveLock = false;
try {
Monitor.Enter(ref haveLock);
// important: Wait releases, waits, and re-acquires the lock
bool wokeEarly = Monitor.Wait(o, timeout);
if(wokeEarly) {...}
} finally {
if(haveLock) Monitor.Exit(o);
}
Another thread could do:
lock(o) { Monitor.PulseAll(o); }
Which will nudge any threads currently in a Wait on that object (but does nothing if no objects were waking). Emphasis: the waiting thread still has to wait for the pulsing thread to release the lock, since it needs to re-acquire.
回答2:
No, the thread won't release the lock
before suspending/sleeping
and no other thread will be able to acquire o
until the sleeping thread wakes up and releases the locked object
回答3:
No, between Enter
and Exit
, no other thread can take the lock whatever you do inbetween.
回答4:
From my experience, calling Thread.Sleep in the middle of a lock block would cause the locking thread to lose the lock (i.e context switch). I ran the following program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
class Program
{
static void Main(string[] args)
{
Class1 c1 = new Class1();
Class2 c2 = new Class2();
Thread t1 = new Thread(c1.DoSomthing);
Thread t2 = new Thread(c2.DoSomthing);
t1.Start();
Thread.Sleep(500);
t2.Start();
}
}
class Class1
{
object m_objSyncLock = new object();
ManualResetEvent m_objSleep = new ManualResetEvent(true);
public void DoSomthing()
{
Monitor.Enter(m_objSyncLock);
int i = 1; //break point here
Thread.Sleep(565);
i++; //break point here
Monitor.Exit(m_objSyncLock);
}
}
}
class Class2
{
object m_objSyncLock = new object();
public void DoSomthing()
{
lock (m_objSyncLock)
{
int i = 1; //break point here
i++;
}
}
}
Add break points to lines 30, 32, 46 and notice that line 32 occur 1st, then line 48, and only then line 34. Doesn't this mean that the Thread.Sleep call made me lose my lock?
Moreover, when using ManualResetEvent.WaitOne instead of Thread.Sleep, the executing thread didn't lose exclusivity (except the switch to ManualResetEvent itself).
I'm no guru but this simple test show that Thread.Sleep might make you lose the lock while using ManualResetEvent.WaitOne keeps the lock code block in sync.
来源:https://stackoverflow.com/questions/9454472/calling-thread-sleep-inside-lock-statement-in-net