将 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
的目的。
我有许多项目,其中后台线程用于执行长时间的 运行 工作,并且使用布尔值来标记线程是否在从 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
的目的。