2012-02-26 95 views
4

我在想,如果一個電話就已經acquiered監視器將臨睡前釋放鎖的線程Threa.Sleep:調用了Thread.Sleep()鎖定語句中的.NET

object o = new object(); 
Montior.Enter(o); 
Thread.Sleep(1000); 
Monitor.Exit(o); 

雖然該線程被暫停 - 可以通過其他線程獲取o

+0

是什麼讓你懷疑? – 2012-02-26 15:55:54

+4

調用,鎖內的Thread.Sleep是無響應應用程序的祕訣。您應儘可能在鎖內執行最少量的工作,否則等待資源的其他線程將堆積等待獲取鎖。 – 2012-02-26 15:56:49

+1

@JaredShaver除了在大量的情況下,例如鎖定將數據發送到從不同線程的負載獲取數據的硬件,但是如果發送數據太快會導致數據崩潰。 – 2015-07-24 09:15:18

回答

4

沒有,如果你睡覺,鎖定不會被釋放。

如果你想想要發佈它,請使用Monitor.Wait(o, timeout);此外,還可以使用它從另一個線程發出信號 - 另一個線程可以使用Monitor.Pulse[All](同時持有鎖)在早於「超時」的時間內喚醒等待的線程(它也將重新獲取進程中的鎖)。

注意:只要使用進入/退出,你應該考慮使用try /終於過 - 或者,如果發生異常,你可能還沒解除鎖定。

例子:

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); 
} 

另一個線程可以這樣做:

lock(o) { Monitor.PulseAll(o); } 

將在該對象上等待當前輕推任意線程(但什麼都不做,如果沒有對象被喚醒)。強調:等待線程仍然需要等待脈衝線程釋放鎖,因爲它需要重新獲取。

+0

但是Pulse方法會自動(和原子地...)釋放對象的鎖,right – 2012-02-27 06:40:45

+0

@PiniSalim no,'Pluse'明確地做**不釋放鎖 - 另一個(等待)線程移動到* ready *隊列,並獲得鎖,並且只有當鎖由'Monitor.Exit'釋放時 - 也就是當它離開「另一個線程可以執行的」lock(o){...} 「例如。 – 2012-02-27 06:48:42

+0

@PiniSalim或來自MSDN,重點mine:「接收到脈衝後,等待的線程移動到就緒隊列。當調用Pulse **的線程釋放鎖**時,就緒隊列中的下一個線程不一定是脈衝線)獲得鎖。「 – 2012-02-27 06:49:36

7

不,在EnterExit之間,沒有其他線程可以鎖定您在其間執行的任何操作。

8

沒有,該線程將不會暫停之前釋放lock /睡眠

,並沒有其他的線程能夠獲得o直到睡眠線程被喚醒和釋放鎖定的對象

3

從我的經驗,調用了Thread.Sleep在鎖塊的中間會導致鎖定線程失去鎖(即上下文切換)。 我跑下面的程序:

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++; 
     } 
    } 
} 

添加斷點到線30,32,46,並注意行32發生第一,然後管線48,然後才線34 不這意味着Thread.Sleep調用使我失去了鎖定?

此外,使用ManualResetEvent.WaitOne代替Thread.sleep代碼時,正在執行的線程沒有失去排他性(除了開關ManualResetEvent的本身)。

我不是大師,但是這個簡單的測試表明Thread.Sleep可能會讓你在使用ManualResetEvent.WaitOne時保持鎖碼同步而失去鎖定。

+0

我不能正確地找出你的觀點,因爲它很難找到行號。如果你可以在每一行中添加一條評論,你就可以說出調試器會發生什麼情況,這將有助於弄清楚你的觀點。 – 2017-08-10 09:58:05

+1

我在代碼 – 2017-08-22 06:43:41

+0

中添加了斷點位置啊現在我看到你錯了。您已創建了2個用於鎖定的對象。那不會做任何事情。 (如果你在2個地方有「新」,這意味着2個對象)。如果你想正確鎖定你應該鎖定在同一個對象上。 – 2017-08-23 08:44:13