等待BackgoundWorker.DoWork()完成

时间:2018-05-18 07:53:29

标签: c# .net multithreading visual-studio

在我们基于.NET Framework的应用程序中,我们使用BackgroundWorker保存一个巨大的文件,以保持UI响应。当我们关闭它时,我们不想在后台停止工作(默认行为)并截断文件。

与此相比,是否存在更优雅的等待完成方式?

while (this.backgroundWorker1.IsBusy)
{
    // Keep UI messages moving, so the form remains 
    // responsive during the asynchronous operation.
    Application.DoEvents();
}

感谢。

编辑:基本上,我们要实现的目标是看到应用程序消失并继续查看进程(Task Manager),直到后台工作完成

3 个答案:

答案 0 :(得分:3)

您可以使用WaitHandle与工作线程保持同步。

private ManualResetEvent _canExit = new ManualResetEvent(true);

private DoBackgroundWork()
{
    _canExit.Reset();
    backgroundWorker1.RunWorkerAsync(_canExit);
}

protected override void OnClosed(EventArgs e)
{
    base.OnClosed(e);

    // This foreground thread will keep the process alive but allow UI thread to end.
    new Thread(()=>
    {
        _canExit.WaitOne();
        _canExit.Dispose();
    }).Start();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    ManualResetEvent mre = (ManualResetEvent )e.Argument;

    // do your work.

    mre.Set();
}

如果您有多个后台线程需要等待,请管理WaitHanlde集合并使用WaitHandle.WaitAll来阻止该进程退出。

答案 1 :(得分:1)

关闭表单(用户看作应用程序)和关闭实际应用程序是有区别的。假设您当前将后台工作器实例作为表单的一部分,则需要将其分成不同的类。您可以使用静态类以简单的方式执行此操作。请注意,这有一个ManualResetEvent,它将在worker完成时被触发,而Action将覆盖worker将调用而不是在表单中指定DoWork事件。

public static class Background
{
    public static ManualResetEvent WorkerResetEvent = new ManualResetEvent(false);

    private static BackgroundWorker worker;
    public static BackgroundWorker Worker
    {
        get
        {
            if (worker == null)
            {
                worker = new BackgroundWorker();
                worker.DoWork += Worker_DoWork;
            }
            return worker;
        }
    }

    private static void Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        WorkerAction?.Invoke();
        WorkerResetEvent.Set();
    }

    public static Action WorkerAction;
}

现在这不是表单的一部分,它可以在表单关闭后保留。然后,您可以在表单关闭后以与您拥有的循环类似的循环保持应用程序运行。解决这个问题最简单的方法就是让父母有一个“父母”。表单未显示但调用现有表单。

public class ParentForm : Form
{
    private void ParentForm_Load(object sender, EventArgs e)
    {
        Visible = false;
        ShowInTaskbar = false;
        new Form1().ShowDialog(); // execution waits until form is closed

        // now wait for worker to finish
        while (Background.Worker.IsBusy)
        {
            Background.ResetEvent.WaitOne(5000); // Waits 5 seconds for the event
        }
}

您需要在Program类中调用ParentForm而不是Form1:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new ParentForm());
}

并初始化您表单中的工作人员。这也会在更新显示时等待工作者,但是当表单关闭时,应该退出循环,上面的循环将接管等待。

public partial class Form1 : Form
{
    private bool closing;
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        closing = true;
        base.OnFormClosing(e);
    }

    private void Save()
    {
        Background.WorkerAction = () =>
        {
            // your saving logic here
        };
        Background.Worker.RunWorkerAsync();

        while (!closing && Background.Worker.IsBusy)
        {
            Background.WorkerResetEvent.WaitOne(500); // wait half a second at a time (up to you)

            // any logic to update progress bar or other progress indicator
            Refresh(); // Update the screen without costly DoEvents call
        }
    }
}

答案 2 :(得分:0)

显然你错过了这个:https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.runworkercompleted(v=vs.110).aspx有一个runworkercompleted - 在完成,取消或异常时被触发

bgworker.RunWorkerCompleted += JobCompleted;

private void JobCompleted(
    object sender, RunWorkerCompletedEventArgs e)
{
    // First, handle the case where an exception was thrown.
    if (e.Error != null)
    {
        MessageBox.Show(e.Error.Message);
    }
    else if (e.Cancelled)
    {
        MessageBox.Show("Job cancelled");
    }
    else
    {
        do_next_stuff();
    }

}