将 BackgroundWorker 更新为任务,其中方法 returns 用于更新 UI 的值

Update BackgroundWorker to Task where method returns a value used to update UI

我有许多项目,其中后台线程用于执行长时间的 运行 工作,并且使用布尔值来标记线程是否在从 UI 更新之前完成long-运行 任务(在一些旧的 NET 3.5 项目中使用的技术)。这是一种不受欢迎的方法,使用 BackgroundWorker 或 Task(首选)更好。

我使用 BackgroundWorker 创建了一个简单的测试项目(见下文)并且它可以工作,但我找不到一个完整的工作示例来使用 Task 执行此操作并且我所做的每次尝试都锁定 UI 或抛出异常。我的问题是如何使用 Task 执行此操作?

这是代码 - 它只是一个带有一个图表和两个按钮的 Winforms 项目:

public partial class Form1 : Form
{
    BackgroundWorker worker = new BackgroundWorker();
    DataProvider dataProvider = new DataProvider();

    private double[] data;

    public Form1()
    {
        InitializeComponent();
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        worker.WorkerReportsProgress = false;
        worker.WorkerSupportsCancellation = true;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        while (chart1.Series.Count > 0) { chart1.Series.RemoveAt(0); }
        ((Button)sender).Enabled = false;
        MessageBox.Show("Please wait");
        worker.RunWorkerAsync();
    }

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            data = dataProvider.ReturnPlotData();
        }
        catch (Exception ex) { MessageBox.Show($"ERROR: {ex.StackTrace.ToString()}"); ; }
    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled) { MessageBox.Show("BackgroundWorker task cancelled"); }
        else if (e.Error != null) { MessageBox.Show($"ERROR during task execution: {e.Error.StackTrace.ToString()}"); }
        else
        {
            try
            {
                UpdatePlot(data);
            }
            catch (Exception ex) { MessageBox.Show($"ERROR: {ex.StackTrace.ToString()}"); ; }
        }
        button1.Enabled = true;
    }

    private void UpdatePlot(double[] data)
    {
        chart1.Series.Add("Data");
        chart1.Series[0].ChartType = SeriesChartType.Line;

        for (int i = 0; i < data.Length; i++) { chart1.Series[0].Points.Add(data[i]); }
    }

    private void Form1_Load(object sender, EventArgs e) { while (chart1.Series.Count > 0) { chart1.Series.RemoveAt(0); } }

    private void button2_Click(object sender, EventArgs e) { MessageBox.Show("I'm doing something else now..."); }
}

internal class DataProvider
{
    public double[] ReturnPlotData()
    {
        Thread.Sleep(10000);
        return new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    }
}

这是一次转换尝试。这给了我一个类型转换错误(Task vs Task double[]):

public partial class Form1 : Form
{
    DataProvider dataProvider = new DataProvider();

    public Form1() { InitializeComponent(); }

    private void Form1_Load(object sender, EventArgs e) { while (chart1.Series.Count > 0) { chart1.Series.RemoveAt(0); } }

    private void button1_Click(object sender, EventArgs e)
    {
        Task<double[]> task = Task.Run(() => 
        {
            dataProvider.ReturnPlotData();
        }).ContinueWith(t => UpdatePlot(task.Result));
    }

    private void button2_Click(object sender, EventArgs e) { MessageBox.Show("I'm doing something else now..."); }

    private void UpdatePlot(double[] data)
    {
        chart1.Series.Add("Data");
        chart1.Series[0].ChartType = SeriesChartType.Line;

        for (int i = 0; i < data.Length; i++) { chart1.Series[0].Points.Add(data[i]); }
    }
}

internal class DataProvider
{
    public double[] ReturnPlotData()
    {
        Thread.Sleep(10000);
        return new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    }
}

您发布的代码可以通过简化代码来工作:

private async void button1_Click(object sender, EventArgs e)
{
    UpdatePlot(await Task.Run((Func<double[]>)dataProvider.ReturnPlotData));
}

您收到错误是因为 ContinueWith() returns 一个 Task 对象。原始 Task<double[]> 对象是传递给您的 ContinueWith() 委托的对象。您不需要从 ContinueWith() 返回的 Task,因此您可以完全省略 task 变量(因此不兼容的赋值)。

但是,您无论如何都不应该使用 ContinueWith()。这就是 await 的目的。