C# 使用async而不是Task.Run()
我有以下代码:C# 使用async而不是Task.Run(),c#,async-await,C#,Async Await,我有以下代码: private void btnAction\u单击(对象发送方,路由目标) { /**清除结果字段*/ txtResult.Text=“”; /**禁用按钮并显示等待状态*/ btnAction.IsEnabled=false; lblStatus.Text=“等待…”; /**从查询字段获取输入*/ 字符串输入=query.Text; /**运行新任务*/ Task.Run(()=>{ //调用需要很长时间(>3s)才能完成并返回的方法 var trunt=someLibra
private void btnAction\u单击(对象发送方,路由目标)
{
/**清除结果字段*/
txtResult.Text=“”;
/**禁用按钮并显示等待状态*/
btnAction.IsEnabled=false;
lblStatus.Text=“等待…”;
/**从查询字段获取输入*/
字符串输入=query.Text;
/**运行新任务*/
Task.Run(()=>{
//调用需要很长时间(>3s)才能完成并返回的方法
var trunt=someLibrary.doSomethingWith(输入);
//将结果返回到GUI thred
this.Dispatcher.Invoke(()=>
{
如果(尝试。包含(“成功”))
{
如果(尝试[“成功”]==true)
{
txtResult.Text=“成功!门是:”+(尝试[“打开”]?“打开”:“关闭”);
lblStatus.Text=“”;
}
其他的
{
lblStatus.Text=“Error!服务说:“+trunt[“errorMessage”];
}
}
其他的
{
Show(“从web服务获取结果时出现问题”);
lblStatus.Text=“”;
}
/**重新启用按钮*/
btnAction.IsEnabled=true;
});
});
}
现在,我想:
- 以使用回调而不是使用
的方式编写相同的代码Dispatcher.Invoke()
- 能够取消调用
doSomething()
- 能够链接多个调用,即等待
,完成后,doSomething()
与上一次调用的结果doAnotherThing()
我该怎么办?您可以将您的方法标记为
async
,等待任务。运行以便在UI上运行continuations,同时只在其中保留长时间运行(似乎CPU受限)的作业
private async void btnAction_Click(object sender, RoutedEventArgs e)
{
btnAction.IsEnabled = false;
txtResult.Text = "";
lblStatus.Text = "Wait...";
string input = query.Text;
// calling a method that takes a long time (>3s) to finish and return
var attempt = await Task.Run(() => someLibrary.doSomethingWith(input));
if (attempt.ContainsKey("success"))
{
if (attempt["success"] == true)
{
txtResult.Text = "Success! The door is: " + (attempt["is_open"] ? "open" : "closed");
lblStatus.Text = "";
}
else
{
lblStatus.Text = "Error! The service says: " + attempt["errorMessage"];
}
}
else
{
MessageBox.Show("There was a problem getting results from web service.");
lblStatus.Text = "";
}
btnAction.IsEnabled = true;
}
更新
要取消该任务,您需要使用CancellationTokenSource
的am实例中的CancellationToken
,并将其传递到任务中。运行以及长时间运行的方法来检查IsCancellationRequested
(如果可以的话)。您可以通过调用CancellationTokenSource来取消。cancel
注意:您可能希望将其包装在try-catch-finally和catch-onoperation-canceledexception
中,并将按钮启用代码放在finally中async
修饰符要求函数返回Task
(或void
,在这种情况下,任何等待语句都将被忽略)。这意味着使用async
和使用Task.Run()
是一样的,你的问题的前提没有意义
但是,我认为您只需要使用asyncwait
语法来避免显式调用Task.Run()
回调
只需创建一个返回任务的函数
async Task<T> Foo()
如果使用两个参数构造任务,则第二个参数是取消标记:
var bar= new Task(action, tokenSource.Token)
这允许您使用
tokenSource.Cancel()
相关链接:
连锁电话
如果不需要定义的执行顺序,可以使用Task.WhenAll()
,否则可以在前一个任务内或在等待执行的代码中执行下一个任务
Task.WhenAll()
:定义“能够链接多个调用”@MichaelRandall:这在Task.Run
中发生,但问题是如何使事件处理程序异步,而不是使用Task.Run
。这种改变的动力可能是因为必须使用Dispatcher.Invoke
返回UI线程。@madreflectionDispatcher.Invoke()
根本不需要使用。如果您var trunt=wait TaskRun(()=>someLibrary.doSomethingWith(input))代码>,您可以使用结果设置UI。这不需要时间。好吧,我只是重复我在上一篇评论中写的内容。这里没有任何东西可以从异步中获益。显然有。最终需要讨论的是能够链接多个调用部分。如果要更新相同的控件,您会链接什么?或者,它还有其他用途?也许这个程序必须随意重复?然后,MessageBox…?我想说的是,除了OP希望删除的任务之外,代码中没有任何内容是可以等待的。我想补充两点:1。使用Task.Factory.StartNew
而不是Task.Run
。(wait Task.Factory.StartNew(…);UpdateUi(…);wait Task.Factory.StartNew(…);UpdateUi(…);…
)2。禁用按钮是第1行!永远!相信我,如果没有,我可以双击。;)@Alb为什么我们要使用StartNew
?这是完美的,因为它删除了对Dispatcher.Invoke()的不必要调用!它还解决了链接调用的问题,因为现在我可以等待Task.Run()
。为了完整性起见,是否有办法取消正在运行的任务(可能引发异常)?@ConfusedGuy您可以使用StartNew,如果您想将长时间运行的任务从线程池中删除,它是一种较旧的方法,有许多细粒度的提示来控制其行为,我个人只会坚持使用较新的任务。现在就运行,除非线程池资源有问题。要取消任务,请将令牌传递到任务。运行确保捕获异常though@ConfusedGuy是的,应该声明一个CancellationTokenSource
,然后获取它的.Token
。我不会让任务处理它,而是直接传递给doSomethingWith()
,作为第二个参数。
var bar= new Task(action, tokenSource.Token)