2014-02-20 54 views
1

我正在啓動一個WinForms應用程序,該應用程序正在啓動多個後臺工作人員。當一個後臺工作人員完成時,如果結果失敗,則會通過ShowDialog(this)方法顯示一個對話框。問題是多個後臺工作者結果失敗時,它會同時顯示多個對話框。我不認爲這是可能的,但它顯然是。我正在閱讀關於消息循環的一些內容,並且似乎即使對話框已打開,消息循環仍在處理消息,這意味着即使對話框已經打開,完成的runworker也會被調用。我雖然可以在對話框中使用「lock(myObject)」,但它似乎沒有,我猜測,因爲同一個線程每次都調用鎖。BackgroundWorker中的ShowDialog RunWorkerCompleted

那麼解決這個問題的適當方法是什麼?我是半個誘惑,只是使用標誌和一個循環是這樣的:

public bool dialogOpen = false; 
public bool cancelMessages = false; 
public void x_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    while (dialogOpen) {} 
    if (cancelMessages) return; 
    dialogOpen = true; 
    MyDialog dlg = new MyDialog("Something went wrong."); 
    if (dlg.ShowDialog(this) == DialogResult.Cancel) cancelMessages = true; 
    dialogOpen = false; 
} 

這會甚至工作?這會導致其他不好的事情發生嗎? (這會阻止消息循環?)

+0

我剛剛嘗試過循環,它阻止了一切。 (對話框不起作用。)我可能不得不建立一個消息隊列。 – teynon

+0

爲什麼不只跟蹤完成後臺線程的數量與陳述的數量以及計數何時下降到0,那麼只有在出現消息框時才顯示消息框 – Franck

+0

正在使用標準的'MessageBox'選項嗎?我測試了一下,那些按照你想要的方式工作。 –

回答

1

您必須從BackgroundWorker()工作線程,DoWork()方法本身內部向用戶詢問。簡單的lock語句將阻止他們嘗試顯示多個對話框。您可以使用Invoke()在主UI線程上正確顯示對話框。

這裏有一個簡單的例子:

private void button1_Click(object sender, EventArgs e) 
    { 
     for(int i = 1; i <=5; i++) 
     { 
      BackgroundWorker x = new BackgroundWorker(); 
      x.DoWork += x_DoWork; 
      x.RunWorkerCompleted += x_RunWorkerCompleted; 
      x.RunWorkerAsync(); 
     } 
    } 

    private bool cancelMessages = false; 
    private Object dialogLock = new object(); 

    void x_DoWork(object sender, DoWorkEventArgs e) 
    { 

     // ... some work ... 
     System.Threading.Thread.Sleep(5000); // five seconds worth of "work" 

     if (true) // some error occurred 
     { 
      lock (dialogLock) // only one worker thread can enter here at a time 
      { 
       if (!cancelMessages) // if error messages haven't been turned off, ask the user 
       { 
        // ask the user on the main UI thread: 
        // *Invoke() is SYNCHRONOUS, so code won't go continue until after "dlg" is dismissed 
        this.Invoke((MethodInvoker)delegate() { 
         MyDialog dlg = new MyDialog("Something went wrong."); 
         if (dlg.ShowDialog(this) == DialogResult.Cancel) 
          cancelMessages = true; 
        }); 
       } 
      } 
     } 
    } 

    public void x_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     Console.WriteLine("RunWorkerCompleted"); 
    } 
+0

我結束了採取不同的路線,但這似乎也會起作用。 – teynon

0

ShowDialog沒有阻止任何東西(所以所有的活動將持續開火),僅在該方法中,您展示模式窗體的代碼,將等待它關閉。

你的想法與變量是差不多好。如果你想顯示所有工人只是一個對話框,然後(不是那麼完美溶液)

public bool dialogOpen = false; 
public void x_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    // check 
    if(dialogOpen) 
     return; 
    dialogOpen = true; 
    (new MyDialog("Something went wrong.")).ShowDialog(this); 
    dialogOpen = false; 
} 

問題就在這裏是競爭條件,發生如果幾個工人將檢查dialogOpen當它尚未設置爲true由任一。如果你想要它是完美的,那就用ManualResetEvent來代替。

但是,您似乎想要什麼所有工作人員將顯示錯誤,但只有一個錯誤的時間。這很難,你的解決方案是錯誤的,因爲你自己封鎖了UI線程。最簡單的方法是防止(阻止)工人自己完成工作,如果其中一個人使用對話框(與以前相同,使用ManualResetEvent

如果你在代碼方面有困難,我明天會幫你。

+0

該對話框更多的是「繼續/忽略」或「取消/退出」選項。因此,如果用戶點擊取消/退出,我們不關心剩下的對話框。 – teynon

0

最後我做一個消息隊列中的布爾檢查。這似乎工作。如果像Sinatr這樣的競爭條件暗示,那麼我不知道爲什麼,據我瞭解,這是在UI線程上的所有處理。

這是我爲了讓它工作而做的切碎版本。

public List<BGWOProcess> messageQueue = new List<BGWOProcess>(); 
public static bool DialogOpen = false; 
public static bool CancelPending = false; 

public void loginProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    BGWOProcess result = (BGWOProcess)e.Result; 

    if (!result.Result) 
    { 
     if (CancelPending) return; 
     if (!DialogOpen) DialogOpen = true; 
     else 
     { 
      messageQueue.Add(result); 
      return; 
     } 

     try 
     { 
      processFailedMessage(result); 
     } 
     catch (Exception) { } 
     DialogOpen = false; 
    } 
    else 
    { 
     //... 
    } 
} 

public void processFailedMessage(BGWOProcess result) 
{ 
    MyMessage msg = new MyMessage("The process " + result.Label + " failed: " + result.Error + " Retry?"); 
    if (msg.ShowDialog(this) == System.Windows.Forms.DialogResult.Yes) 
    { 
     // Retry request. 
     Queue(result.func, result.Label, result.progressIncrement); 

     if (messageQueue.Count > 0) 
     { 
      BGWOProcess nextMessage = messageQueue[0]; 
      messageQueue.Remove(nextMessage); 
      processFailedMessage(nextMessage); 
     } 
    } 
    else 
    { 
     r = false; 
     CancelPending = true; 

     // Fail. 
     DialogResult = System.Windows.Forms.DialogResult.Abort; 
    } 
} 
相關問題