2012-03-29 6 views
1

フォームを閉じるために実行中にBackgroundWorker DoWork()プロセスを完全に停止したいとします。バックグラウンドワーカーを停止してフォームを閉じるには?

私は次のコードを適用しましたが、 "this.Invoke"では "ウィンドウハンドルが作成されるまで、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 void Form_KeyDown(object sender, KeyEventArgs e) 
{ 
    if (e.KeyData == Keys.Escape) 
    { 
     Close(); 
    } 
} 

FormClosingの内部では、労働者が既に持っているかどうかを確認:あなたは、フォームを閉じるだけでCloseを呼び出したいとき、そして、

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

を停止。この部分は、バックグラウンドスレッドがちょうど完成されている競合状態を防ぐために、モニター内でなければならない(すなわち_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); 
} 

最後に、あなたのバックグラウンドスレッドでは、実際に近いです必要に応じてフォーム:あなたは次の画像の処理を開始する前にbackgroundWorker1.CancellationPendingに対してチェックする必要があり、あなたのDoWorkメソッド内

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

なぜbackgroundWorker1上AutoResetEventを選択してください。 CancelAsync()?利点はありますか?ちょっと興味があるんだけど。 – RvdK

+0

@PoweRoy:あなたは正しいです。最初の部分は、ワーカーメソッド内で 'CancelAsync'を呼び出して' BackgorundWorker.CancellationPending'をポーリングすることによっても行うことができます。これはほんの少し一般的なもので、単純なバックグラウンドスレッドがワーカとして使用されているときに機能します。 – Groo

+0

このプロセスを使用していますが、このプロセスでは "_workerStopped.WaitOne();"カーソルは待機中です。 @Groo – Tulsi

0

ユーザーがフォームを閉じる前に、最後のイメージが処理されるまで待つ必要があります。 CancellationPendingCancelプロパティを使用する方法のcomlete例えば

は、あなたがあなたの労働者内部の取り消し要求を監視する必要がthis MSDN example

+0

私もMSDNの例を使用しましたが、同じ問題が発生します。@ Philip Fourie – Tulsi

2

を参照してください。

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

while (!worker.CancellationPending) 
{ 
    … 
} 

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

それでも十分ではありません。処分する前に労働者が本当に停止していないかどうかを確認しなければ、毎回例外が発生する競合状態があります。 – Groo

+0

上記のコードを適用しましたが、同じ問題が発生しました。@ Israel Lot – Tulsi

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; 
} 
関連する問題