2011-07-14 28 views
0

下面是一個代碼示例,演示了我在Windows CE應用程序中使用的Windows窗體的設計。關於.NET中的線程

我們在應用程序中遇到了一些未解決的問題,我懷疑問題來自此處用作後臺工作者的線程(因爲BackgroundWorker類在Windows CE中不可用)。

您可以看到鎖對象用於防止多個實例MyWorker

這是防止這種「工人」的多個實例的正確方法?它會按預期工作嗎?單身職員會更好嗎?

public class MainForm : Form { 
    private object myLock = new Object(); 
    private bool isWorkerStarted = false; 

    private Thread worker; 

    public MainForm() { 
    } 

    public void btn_Click() { 
     lock(myLock) { 
      if(!isWorkerStarted) { 
       MyWorker worker = new MyWorker(); 
       worker.StartEvent = new EventHandler(ThreadStart); 
       worker.EndEvent = new EventHandler(ThreadStop); 
       workerThread = new Thread(worker.DoWork); 

       workerThread.Start(); 

       isWorkerStarted = true; 
      } 
     } 
    } 

    public void ThreadStart(object sender, EventArgs args) { 
     lock(myLock) { 
      isWorkerStarted = true; 
      // Invoke some delegate to interact with the window 
     } 
    } 

    public void ThreadStop(object sender, EventArgs args) { 
     lock(mylock) { 
      isWorkerThread = false; 
     } 

     this.Invoke(new NewFormDelegate(OpenForm)); 
    } 

    private void OpenForm() { 
     AnotherWindowForm awf = new AnotherWindowForm(); 
     awf.Show(); 
     this.Close(); 
    } 

    //****************** 
    // Worker class 
    //****************** 
    public class MyWorker() { 
     public event EventHandler StartEvent; 

     public void OnStart() { 
      if(StartEvent != null) { 
        StartEvent(this, new EventArgs()); 
      } 
     } 

     // Edit 2011-07-19 
     public void OnEnd() { 
      if(EndEvent != null) { 
        EndEvent(this, new EventArgs()); 
      } 
     } 

     public void DoWork() { 
      OnStart(); 
      // Do some work. 
      OnEnd(); 
     } 
    } 

}

編輯2011-07-19 以下是有關該項目的目標的更多細節。

我們有一個Windows窗體只有一個Windows CE設備上的按鈕。當用戶點擊按鈕時,必須啓動一個工作線程,請求一個WCF服務。在執行線程期間,小沙漏向用戶指示程序正忙。當工作完成時(即,當他收到「良好」答案時),它必須停止並打開當前窗體頂部的另一個Windows窗體(爲此目的,我使用方法Invoke委派創建新窗體形成)。

唯一的要求是,用戶應該不能運行兩個工人(例如通過點擊按鈕兩次)。事實是,有時候,我們可以看到兩名工作人員正在使用同一臺設備向WCF服務發送請求。

+0

你還沒說你有什麼問題。另外,C#編程語言沒有特定於線程的構造。你實際上在問.NET線程,而不是C#線程。 –

+0

由於整個應用程序使用WCF,可能成爲我們問題的根源,所以只用一個代碼示例來解釋問題有點難。這篇文章的目標是確保代碼的這部分是「安全的」。我改變了標題。 – Goulutor

+0

我的意思是你沒有告訴我們你想要解決什麼問題。 –

回答

0

我不會在這種情況下使用lock()。如果後臺工作者已經在運行,那麼主GUI線程將位於該鎖上,直到另一個線程完成。

我會推薦使用互斥鎖。這樣,您可以將其設置爲自動跳過該部分,如果它已被鎖定。

所以,你應該這樣做:

Mutex mtx = new Mutex(); 

public void btn_Click() { 
    if (mtx.WaitOne(0) == true) 
    { 
     //Do your stuff 
    } 
    else 
    { 
     //Let user know you can't do this yet? Or, queue it up? Or, why 
     // can the user click this button if they can't do anything anyway? 
    } 
} 

你需要做一個mtx.Release(),只要您的線程完成做它的東西。

+0

如果使用正確,鎖定不會阻止UI,因爲它只應用於通過設置isWorkerStarted標誌來防止競爭條件。真正的問題是線程獲取並持有鎖,當它實際上不需要這樣做時。 –

+0

嗯,現在我回頭看看,這是真的。如果他只是移動它'//調用一些委託來與窗口交互'在鎖外應該沒問題。由於某種原因,我完全錯過了isWorkerStarted布爾,並認爲他使用鎖來保持多個實例運行。哦,我的工作也是! :) – DanTheMan

+0

您還可以使用現有的SyncBlock(鎖語句的實現機制)來實現此模式,因爲有Monitor.TryEnter方法。棘手的是,如果你能夠獲得它,確保你總是釋放鎖。我相信這比Mutex輕一些,但Mutex也應該可以正常工作。 –

0

根本不需要使用鎖,因爲btn_Click方法只能由UI線程調用(除非您明確地從另一個線程調用該方法,否則會導致錯誤)。

所以代碼只需要一個null檢查,以確保您只有一個工作線程創建。

public class MainForm : Form 
{ 
    private Thread workerThread = null; 

    public MainForm() 
    { } 

    public void btn_Click() 
    { 
     if (workerThread == null) 
     { 
      var worker = new MyWorker(); 
      worker.StartEvent += (s, e) => 
      { 
       //Invoke some delegate to interact with the window 
      }; 
      workerThread = new Thread(worker.DoWork); 
      workerThread.Start(); 
     } 
    } 
} 

編輯:在響應於來自OP註釋。

public class MainForm : Form 
{ 
    private Thread workerThread = null; 

    public MainForm() 
    { } 

    public void btn_Click() 
    { 
     if (workerThread == null || workerThread.IsAlive == false) 
     { 
      var worker = new MyWorker(); 
      worker.StartEvent += (s, e) => 
      { 
       //Invoke some delegate to interact with the window 
      }; 
      worker.EndEvent += (s, e) => 
      { 
       //Clean up 
      }; 
      workerThread = new Thread(worker.DoWork); 
      workerThread.Start(); 
     } 
    } 
} 

而且MyWorker現在看起來是這樣的:

public class MyWorker 
{ 
    public event EventHandler StartEvent; 
    public event EventHandler EndEvent; 

    public void OnStart() 
    { 
     var se = this.StartEvent; 
     if (se != null) 
     { 
      se(this, new EventArgs()); 
     } 
    } 

    public void OnEnd() 
    { 
     var ee = this.EndEvent; 
     if (ee != null) 
     { 
      ee(this, new EventArgs()); 
     } 
    } 

    public void DoWork() 
    {   
      this.OnStart(); 
      // do some work 
      this.OnEnd(); 
    } 
} 
+0

有了您的解決方案,我該如何檢查工人是否已完成工作,因爲我希望能夠在此之後創建另一名工人? – Goulutor

+0

@Goulutor - 你的原始代碼沒有任何檢查線程何時結束的方式,你在你的問題中說你不想要多個工人實例。我感覺你的問題不完全是「代碼完整」。給我打個勾,我會編輯我的解決方案。 – Enigmativity

+0

我正在使用Compact Framework(在Windows CE上),因此對於類Thread而言,屬性IsAlive不存在。 – Goulutor