Как остановить Backgroundworker и закрыть форму? - PullRequest
2 голосов
/ 29 марта 2012

Я хочу полностью остановить процесс 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();  
            }
        }

Как решить эту ошибку?

, пожалуйста, помогите мне.

Ответы [ 4 ]

2 голосов
/ 29 марта 2012

Вы должны следить за запросом отмены внутри вашего работника.Нравится

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

 while (!worker.CancellationPending) 
 { 
   … 
 } 

 if (worker.CancellationPending) 
 { 
  e.Cancel = true; 
 } 
}
1 голос
/ 30 марта 2012

Вот простая программа, которая, я думаю, даст вам представление о том, как позаботиться о закрытии формы во время работы 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;
}
1 голос
/ 29 марта 2012

Реальная проблема в том, что нет способа безопасно проверить, закрыта ли форма, перед вызовом 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 голосов
/ 29 марта 2012

Внутри вашего метода DoWork вы должны проверить значение backgroundWorker1.CancellationPending перед началом обработки следующего изображения.

Вам нужно подождать, пока последнее изображение будет обработано, прежде чем позволить пользователю закрыть форму.*

Более подробный пример использования свойств CancellationPending и Cancel см. в этом примере MSDN

...