【问题标题】:Update BackgroundWorker to Task where method returns a value used to update UI将 BackgroundWorker 更新为 Task,其中方法返回用于更新 UI 的值
【发布时间】:2018-01-31 18:37:12
【问题描述】:

我有许多项目,其中后台线程用于执行长时间运行的工作,并且在从长时间运行的任务更新 UI 之前使用 bool 来标记线程是否已完成(在一些旧的任务中使用的技术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 };
    }
}

【问题讨论】:

  • 您能否发布一些使其基于任务的努力?这样我们就有了一个工作的起点,即使他们现在失败了。
  • “这给了我一个类型转换错误(Task vs Task double[])”——你从哪里得到这个错误? 确切地说的错误是什么?另外,请注意您应该使用private async void button1_Click(...),然后不要显式使用ContinueWith(),而是可以使用UpdatePlot(await Task.Run(dataProvider.ReturnPlotData));。 (确实,现在我知道你会在哪里得到那个错误......ContinueWith() 方法返回一个 Task......你甚至不需要,所以 task 变量可以完全省略。但是使用async/await 反正我说过)

标签: c# winforms task backgroundworker


【解决方案1】:

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

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

您收到错误是因为 ContinueWith() 返回一个 Task 对象。原始的 Task&lt;double[]&gt; 对象是传递给您的 ContinueWith() 代表的内容。您不需要从ContinueWith() 返回的Task,因此您可以完全省略task 变量(因此不兼容的赋值)。

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

【讨论】:

  • 抱歉...缺少minimal reproducible example 我没有费心编译提议的新代码。您应该能够通过显式转换参数来解决错误(请参阅上面的编辑)
  • 这个解决方案有效,所以我接受了它。我看不出我提供的代码不是 MCVe - 我包含了除表单设计器之外的整个代码,并且我描述了设计(1图表和 2 个按钮)。
  • 您的问题中有足够的信息来提供答案(显然)。但是 MCVE 可以严格按原样编译。当我看到像chart1 这样的东西时,我猜你是如何配置你的程序的,我不打扰。这需要很长时间,而且我不能保证它会正确。这个问题肯定比很多问题好,但是我花时间让 IDE 参与其中是不够的(尤其是对于像这样一个相对简单的问题)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-23
相关资源
最近更新 更多