2012-03-29 112 views
1

我想完全停止BackgroundWorker DoWork()過程,同時運行關閉窗體。如何停止Backgroundworker並關閉表單?

我已經應用了下面的代碼,但在「this.Invoke」中會引發錯誤:「調用或BeginInvoke無法在控件上調用,直到窗口句柄被創建。」同時形成關閉。

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
     { 
      var dt_Images = db.Rings.Select(I => new { I.PaidRs, I.TypeID, I.RingID, I.CodeNo, Image = Image.FromStream(new MemoryStream(I.Image.ToArray())) }).OrderByDescending(r => r.TypeID); 
      foreach (var dr in dt_Images.ThenByDescending(r => r.RingID).ToList()) 
      { 
       BTN = new Button(); 
       BTN.TextImageRelation = TextImageRelation.TextAboveImage; 
       BTN.TextAlign = System.Drawing.ContentAlignment.BottomCenter; 
       BTN.AutoSize = true; 
       BTN.Name = dr.RingID.ToString(); 
       BTN.Image = dr.Image; 
       BTN.Text = dr.CodeNo.ToString() + " " + dr.TypeID.ToString(); 
       this.Invoke(new MethodInvoker(delegate { if (backgroundWorker1 != null) flowLayoutPanel1.Controls.Add(BTN); else return; })); 
       BTN.Click += new EventHandler(this.pic_Click); 
       this.Invoke(new MethodInvoker(delegate { if (backgroundWorker1 == null) txt_pcs.Text = flowLayoutPanel1.Controls.Count.ToString(); else return;})); 
      } 
     } 

    private void Form_KeyDown(object sender, KeyEventArgs e) 
     { 
      if (e.KeyData == Keys.Escape) 
      { 
       backgroundWorker1.CancelAsync(); //backgroundworker doesnt stop here 
       backgroundWorker1 = null; //it still invokes the delegate 
       this.Dispose(); 
      } 
     } 

如何解決此錯誤?

請幫幫我。

回答

1

實際的問題是,在後臺線程調用Invoke之前,沒有辦法安全地檢查表單是否正在關閉。

要做到這一點,你可以做的是推遲關閉一點,直到後臺線程有機會退出主循環。

首先,聲明兩個標誌和一個鎖對象:

private volatile bool _closeRequest = false; 
private volatile bool _workerStopped = false; 
private readonly object _lock = new object(); 

然後,當你想關閉的形式,只需撥打Close

private void Form_KeyDown(object sender, KeyEventArgs e) 
{ 
    if (e.KeyData == Keys.Escape) 
    { 
     Close(); 
    } 
} 

裏面FormClosing,檢查是否勞動者已停止。這部分必須是顯示器內部,以防止在後臺線程只是被完成了比賽條件(即確保_workerStopped_closeRequest是原子更新):

protected override void OnFormClosing(FormClosingEventArgs e) 
{ 
    lock (_lock) 
    { 
     // if not stopped 
     if (!_workerStopped) 
     { 
      // delay closing 
      e.Cancel = true; 

      // notify worker 
      _closeRequest = true; 
     } 
    } 
    base.OnFormClosing(e); 
} 

最後,在你的後臺線程,實際關閉如果需要的形式:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    try 
    { 
     foreach (var dr in GetImages()) 
     { 
      if (_closeRequest) 
       break; 

      // ... do stuff 
     } 
    } 
    finally 
    { 
     lock (_lock) 
     { 
      // notify we stopped 
      _workerStopped = true; 

      // if closing was postponed, close now 
      if (_closeRequest) 
       BeginInvoke(new MethodInvoker(Close)); 
     } 
    } 
} 
+0

爲什麼選擇的AutoResetEvent在backgroundWorker1。 CancelAsync()?有沒有好處?只是好奇。 – RvdK 2012-03-29 08:04:59

+0

@PoweRoy:你說得對,第一部分也可以通過調用'CancelAsync'並在worker方法內輪詢'BackgorundWorker.CancellationPending'完成。這只是稍微更通用一些,並且在將簡單的後臺線程用作工作人員時可以工作。 – Groo 2012-03-29 08:08:30

+0

使用此方法,但在此過程中「_workerStopped.WaitOne();」光標正在等待。 @Groo – Tulsi 2012-03-29 08:54:39

0

裏面你必須開始處理下一個圖像之前要檢查你的backgroundWorker1.CancellationPending方法的DoWork。

在允許用戶關閉表單之前,您必須等待處理完最後一張圖像。

有關如何使用CancellationPendingCancel性能comlete例子中看到this MSDN example

+0

我也使用過MSDN的例子,但是也出現過同樣的問題@ Philip Fourie – Tulsi 2012-03-29 08:57:15

2

你必須注意你的工人裏面的取消請求。像

private void DoWork(object sender, DoWorkEventArgs e) 
{ 
var worker = sender as BackgroundWorker; 

while (!worker.CancellationPending) 
{ 
    … 
} 

if (worker.CancellationPending) 
{ 
    e.Cancel = true; 
} 
} 
+0

這還不夠。如果你在處理之前沒有檢查員工是否真的停下來,那麼競爭狀態每隔一段時間就會引起異常。 – Groo 2012-03-29 08:15:21

+0

我已經應用上面的代碼,但得到同樣的問題。@以色列地段 – Tulsi 2012-03-29 08:51:38

1

這裏有一個簡單的程序,我想,一定會給你的同時BackgroundWorker運行如何採用這樣的形式的關閉的護理的想法:

int i = 0;//This is to show progress. 
bool cancel;//This is to notify RunWorkerCompleted to Close() the Form if needed. 

public Form1() 
{ 
    InitializeComponent(); 
    FormClosing += Form1_FormClosing; 

    backgroundWorker1.WorkerSupportsCancellation = true; 
    backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted; 
    backgroundWorker1.RunWorkerAsync(); 
} 

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    //Since this is executed on the main thread - it is not (as far as I know) going to "race" against the FormClosing. 
    if (cancel) Close(); 
    else Text = "Done"; 
} 

void Form1_FormClosing(object sender, FormClosingEventArgs e) 
{ 
    if (backgroundWorker1.IsBusy) 
    { 
     cancel = true; 
     backgroundWorker1.CancelAsync(); 
     e.Cancel = true; 
    } 
} 

private void button1_Click(object sender, EventArgs e) 
{ 
    backgroundWorker1.CancelAsync(); 
} 

//This is executed on a separate thread: 
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while (!backgroundWorker1.CancellationPending) 
    { 
     Invoke((Action)(() => Text = (i++).ToString())); 
     Thread.Sleep(1000); 
    } 
    e.Cancel = true; 
}