在C中异步执行多个任务# 环境 视窗7 Visual Studio C# 我想做什么
我正在尝试建立一个应用程序来评估公司的产品。为了安全起见,以下描述在某种程度上是抽象的 此应用程序所做的是更改产品中的某个参数,并查看产品的某个值是如何更改的。所以我需要做两件事在C中异步执行多个任务# 环境 视窗7 Visual Studio C# 我想做什么,c#,asynchronous,C#,Asynchronous,我正在尝试建立一个应用程序来评估公司的产品。为了安全起见,以下描述在某种程度上是抽象的 此应用程序所做的是更改产品中的某个参数,并查看产品的某个值是如何更改的。所以我需要做两件事 按一定间隔更改参数 以一定间隔在文本框中显示值 图表是这样的 应重复执行这些任务,直到按下“取消”按钮 UI具有以下控件: 按钮1:开始按钮 按钮2:取消按钮 textbox1:显示从设备获取的值 这是我写的代码 public partial class Form1 : Form { public For
- 按钮1:开始按钮
- 按钮2:取消按钮
- textbox1:显示从设备获取的值
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cts = new CancellationTokenSource();
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
private async Task Task1()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
private void ChangeParameter(double param)
{
// change device paremeter
Console.WriteLine("devicep parameter changed : " + param);
}
private async Task Task2()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(100);
int data = GetDataFromDevice();
UpdateTextBoxWithData(data);
}
cts.Token.ThrowIfCancellationRequested();
}
private int GetDataFromDevice()
{
//pseudo code
var rnd = new Random();
return rnd.Next(100);
}
private void UpdateTextBoxWithData(int data)
{
textBox1.AppendText(data.ToString() + "\n");
// debug
Console.WriteLine("data : " + data);
}
private void button2_Click(object sender, EventArgs e)
{
cts.Cancel();
}
}
问题
但是,此代码中有两个问题
wait
,因为它逐个执行任务。我可以使用Task.Run()
,但这不允许向textBox添加值,因为它与UI线程不同
如何解决这些问题?任何帮助都将不胜感激。问题在于您没有在任务中使用
wait
,因此它们会同步执行
您应该使用类似的方法来保持UI的响应性(注意这不是生产代码,我只是展示一个想法):
基本上,当您需要在后台运行某些东西时,应该将其包装在Task.run()
中,然后等待结果。简单地将async
添加到您的方法不会使此方法异步
为了使您的代码更清晰,我建议您将GetDataFromDevice
或ChangeParameter
等方法移动到服务层。另外,请查看IProgress
,因为注释建议根据某些流程的进度更新UI。问题在于您没有在任务中使用wait
,因此它们会同步执行
您应该使用类似的方法来保持UI的响应性(注意这不是生产代码,我只是展示一个想法):
基本上,当您需要在后台运行某些东西时,应该将其包装在Task.run()
中,然后等待结果。简单地将async
添加到您的方法不会使此方法异步
为了使您的代码更清晰,我建议您将GetDataFromDevice
或ChangeParameter
等方法移动到服务层。另外,请查看IProgress
,评论建议您根据某些流程的进度更新UI。此代码存在许多问题:
async/await
不会自动使代码异步。它允许您等待已经异步的操作的结果。如果要在后台运行尚未异步的内容,则需要使用Task.run或类似的方法启动任务
wait
将执行返回到原始同步上下文。在本例中,UI线程。通过使用Thread.Sleep,您正在冻结UI线程
您不能从其他线程更新UI,这也适用于任务。您可以使用IProgress界面来报告进度。很多BCL类都使用这个接口,就像CancellationToken一样
Maxim Kosov已经清理了代码,并演示了如何正确使用async/await
和Task.Run,所以我只介绍如何使用及其实现
IProgress用于使用该方法公开进度更新。它的默认实现Progress在UI线程上引发事件和/或调用传递给其构造函数的操作。特别是,在创建类时捕获的同步上下文上
您可以在构造函数或按钮单击事件中创建进度对象,例如
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<int>(data=>UpdateTextBoxWithData(data));
//...
//Allow for cancellation of the task itself
var token=cts.Token;
await Task.Run(()=>MeasureInBackground(token,progress),token);
}
private async Task MeasureInBackground(CancellationToken token,IProgress<int> progress)
{
while (!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(data);
}
}
在这里,我只是懒洋洋地返回一个元组。专用的进度类在生产代码中更合适
使用动作的优点是不需要管理事件处理程序,并且对象是异步方法的本地对象。清理由.NET本身执行
如果设备API提供真正的异步调用,则不需要Task.Run
。这意味着您不必在tigh循环中浪费任务,例如:
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100, token);
int data = await GetDataFromDeviceAsync();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
此代码存在许多问题:
async/await
不会自动使代码异步。它允许您等待已经异步的操作的结果。如果要在后台运行尚未异步的内容,则需要使用Task.run或类似的方法启动任务
wait
将执行返回到原始同步上下文。在本例中,UI线程。通过使用Thread.Sleep,您正在冻结UI线程
您不能从其他线程更新UI,这也适用于任务。您可以使用IProgress界面来报告进度。很多BCL类都使用这个接口,就像CancellationToken一样
Maxim Kosov已经清理了代码,并演示了如何正确使用async/await
和Task.Run,所以我只介绍如何使用及其实现
IProgress用于使用该方法公开进度更新。它的默认实现Progress在UI线程上引发事件和/或调用传递给其构造函数的操作。特别是,在创建类时捕获的同步上下文上
您可以在构造函数或按钮单击事件中创建进度对象,例如
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<int>(data=>UpdateTextBoxWithData(data));
//...
//Allow for cancellation of the task itself
var token=cts.Token;
await Task.Run(()=>MeasureInBackground(token,progress),token);
}
private async Task MeasureInBackground(CancellationToken token,IProgress<int> progress)
{
while (!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(data);
}
}
在这里,我只是懒洋洋地返回一个元组。专用的进度类在生产代码中更合适
美国的优势
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100, token);
int data = await GetDataFromDeviceAsync();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
await MeasureInBackground(token,progress);
private async Task Task1()
{
await Task.Run(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
}
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
private async void button1_Click(object sender, EventArgs e)
{
Task t1 = Task1();
Task t2 = Task2();
await Task.WhenAll(new[] { t1, t2 });
}
// await Task.Run(() => LooongOperation(), token);
await Task.Factory.StartNew(() => LooongOperation(), token, TaskCreationOptions.LongRunning, TaskScheduler.Default);