2013-10-29 16 views
-1

EDIT2 - > 看看底部; < - EDIT2c#lock()掛在第二次鎖定嘗試

我遇到了奇怪的(對我來說,至少)行爲。

我甚至創建了簡單的WinForms類和簡單的類(下面的代碼)來測試它。

我一直認爲打電話lock(m_lock)如果以前的lock(m_lock)通話沒有結束,第一個會等待並進入第二個鎖定範圍。不。

的動作流程是:

  1. 創建Class1對象;

  2. 調用Start()方法;

  3. 調用DoSomething()方法,而m_lock被鎖定在run方法;

輸出是:

起動()

試圖獲取鎖

獲得的鎖

發行鎖

試圖獲取鎖

獲得的鎖

DoSomething的()試圖獲取鎖

... ...掛起

我缺少什麼或者做錯了嗎?我是C#的新成員(來自C++),因此在C#中可能有一些陷阱

它仍然掛...(由時間我最後寫這篇文章)

編輯 - > 在現實世界中我用鎖來保護讀/寫/配置上的SerialPort(帶同步讀/寫,而不是異步)。我在dbg中看到有一些內部的WaitOne調用。不知道它是否相關。 < --edit

這裏的例子:

using System; 

namespace LockTester 
{ 
    public class Class1 
    { 
     object m_lock = null; 
     bool m_isRunning; 
     System.Threading.Thread m_thread = null; 
     public Class1() 
     { 
      Console.WriteLine("Class1 ctor"); 
      m_lock = new object(); 
      m_isRunning = false; 
     } 

     public void DoSomething(){ 
      Console.WriteLine("DoSomething() Trying to acquire lock"); 
      lock(m_lock){ 
       Console.WriteLine("DoSomething() Acquired lock"); 
      } 
      Console.WriteLine("DoSomething() Released lock"); 
     } 

     public void Start(){ 
      Console.WriteLine("start()"); 
      m_isRunning = true; 
      if (m_thread == null){ 
       m_thread = new System.Threading.Thread(Run); 
      } 
      m_thread.Start(); 
     } 

     public void Stop(){ 
      Console.WriteLine("stop()"); 
      m_isRunning = false; 
     } 

     private void Run(){ 
      while (m_isRunning){ 
       Console.WriteLine("Trying to acquire lock"); 
       lock(m_lock){ 
        Console.WriteLine("Acquired lock"); 
        System.Threading.Thread.Sleep(1000); 
       } 
       Console.WriteLine("Released lock"); 
       System.Threading.Thread.Sleep(1000); 
      } 
     } 
    } 
} 

EDIT2:

好了,找到了答案。這是一個更常見的分母。我已經找到了一個解決方案將控制檯輸出重定向到TextBox(純粹爲了測試的原因,你知道 - 小測試應用程序可以捕獲測試對象的內部消息被打印到控制檯)。

下面的代碼:我在窗體的構造函數中使用

_writer = new TextBoxStreamWriter(textBox1, this);

Console.SetOut(_writer);

public class TextBoxStreamWriter : TextWriter 
{ 
    TextBox _output = null; 
    Form _form = null; 
    object _lock = new object(); 

    delegate void SetTextCallback(string text); 

    private void SetText(string text) 
    { 
     // InvokeRequired required compares the thread ID of the 
     // calling thread to the thread ID of the creating thread. 
     // If these threads are different, it returns true. 
     if (_output.InvokeRequired) 
     { 
     SetTextCallback d = new SetTextCallback(SetText); 
     _form.Invoke(d, new object[] { text }); 
     } 
     else 
     { 
      _output.AppendText(text); 
     } 
    } 


    public TextBoxStreamWriter(TextBox output, Form form) 
    { 
     _output = output; 
     _form = form; 
    } 

    public override void Write(char value) 
    { 
     lock (_lock) 
     { 
      base.Write(value); 
      SetText(value.ToString()); 
     } 
    } 

    public override Encoding Encoding 
    { 
     get { return System.Text.Encoding.UTF8; } 
    } 
} 

任何人都可以解釋我爲什麼這引起了這個問題?

+0

根據您的編輯,似乎您的一個鎖定塊內的串行I/O代碼被阻止。爲什麼它停滯不前,很難猜測沒有看到實際的代碼... – elgonzo

+0

這段代碼對我來說是完美的。 –

+0

@SimonWhitehead - 它是平臺/配置特定的嗎?我使用4.0CP的Target Framework運行它,但「真正的」運行到3.5 ...這裏的平分母是我的機器。 – jaor

回答

2

當你調用Form.Invoke,它會做到這一點:

在擁有此控件的基礎窗口句柄的線程上執行指定的委託。

這樣做的方式是將消息發佈到擁有線程的消息隊列中,然後等待該線程處理該消息。

因此,Invoke阻止直到被調用的委託被調用纔會返回的調用。

現在,您的代碼被阻止的可能原因是您的主GUI線程已在等待其他事情發生,可能是您的外部程序已完成。

因此,它實際上並不處理消息。

如果這是原因,那麼這裏的解決方案是刪除GUI線程的阻塞部分。不要坐在等待外部程序完成的地方,而是分出一個等待它完成的任務,然後在主窗體上提供適當的事件。與此同時,主線程可自由處理消息,更新文本框等。

請注意,這意味着如果啓動外部程序是爲了響應某個事件而完成的,例如點擊按鈕,則可能需要在程序運行時禁用部分用戶界面,以避免讓用戶點擊兩次按鈕,啓動兩個並行執行,這兩個執行都會報告給同一個文本框。

結論:多線程編程很難!

相關問題