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-on
operation-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线程。@madreflection
Dispatcher.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)