【问题标题】:Task async and await任务异步和等待
【发布时间】:2016-04-12 01:04:47
【问题描述】:

我希望 PrintOrderAsync 方法执行外部 EXE 以打印订单。 方法 PrintOrderAsync 的代码没有显示任何语法错误

public async Task<string> PrintOrderAsync(string PrintExe, string ExePath, int nOrderNo)
{
    await Task.Factory.StartNew(() => Process.Start(ExePath + PrintExe, nOrderNo.ToString()));

        return "";
 }

但我正在为调用方法的语法而苦苦挣扎。这是我尝试过的:

Task<string> result = PrintOrderAsync(PrintExe, ExePath, nOrderNo);

我在上面的行中看到了语法错误。我错过了什么?

【问题讨论】:

  • 你为什么会选择使用StartNew?你在某处看到过这个例子吗?
  • @StephenCleary 是的,我确实在某处看到了这个。但我在听,如果您认为代码应该更好地适应更改,请告诉我。
  • @StephenCleary 既然您提出了这个问题,我想看看如何使用 StartNew 更改/优化该行,但无法弄清楚这一点。显然你发现我的语法有问题,所以我需要学习如何让它更好地工作。
  • @Hidalgo,您真的对Process.Start 的即发即弃语义感到满意吗?您没有在返回的 Process 实例上调用 WaitForExit 的任何特殊原因?
  • 使用TaskCompletionSource查看this问题中的答案以获得更好的实施

标签: asp.net asynchronous async-await


【解决方案1】:

根据您在此处共享的代码,您正在启动一个进程。这是令人担忧的,因为您实际上并没有等待进程的结果——事实上,不管asyncawait 关键字是什么,除非您等待进程退出,否则它就是一劳永逸。有几种方法可以做到这一点:

public static Task WaitForExitAsync(this Process process, 
    int milliseconds,
    CancellationToken cancellationToken = default(CancellationToken))
{    
    return Task.Run(() => process.WaitForExit(milliseconds), cancellationToken);
}

例如,您可以使用以下扩展方法将等待进程包装在Task 中。然后可以像这样等待:

public async Task PrintOrderAsync(string PrintExe, string ExePath, int nOrderNo)
{
    return Process.Start(ExePath + PrintExe, nOrderNo.ToString())
                  .WaitForExitAsync(5000);
}

// Then you could await it wherever...
await PrintOrderAsync(PrintExe, ExePath, nOrderNo);

或者,如果您不想等待它完成(即;您希望像现在这样,一劳永逸),请执行以下操作:

Process.Start(ExePath + PrintExe, nOrderNo.ToString())

不要将其包装在 Task 或任何东西中,无论如何它是一个完全独立的过程(就我个人而言,我更喜欢我分享的第一个选项)。

【讨论】:

  • 大卫,首先感谢您的帖子和代码。有些事情让我感到困惑(当然是我)。如果我根本不使用异步和等待(正如您所指出的那样)并简单地使用 Process.Start(ExePath + PrintExt, nOrderNo.ToString()) 启动进程,那么这个进程不会在与“相同的线程上启动”主”代码,“主”代码是否必须等待进程完成?也就是说,我认为使用 async 和 await 的原因是将进程“移动”到新线程,因此“释放”“主”线程以继续。逻辑错误? :)
  • 一点也不,它会启动一个与启动它的代码完全断开的新进程。试试这个,编写一个简单的控制台应用程序来执行Process.Start("notepad")。并立即退出控制台应用程序——记事本保持打开状态并在自己的进程中启动——实际上这两个进程根本无法共享线程。
  • 大卫,非常感谢。你说的对。希望学习如何使用 async 和 await 的整个练习并没有完全浪费时间,我会在某个时候使用它。
【解决方案2】:

试试

string result = await PrintOrderAsync(PrintExe, ExePath, nOrderNo);

【讨论】:

  • M Tovbin,我承认我对我在这里提出的话题几乎一无所知。因此,我不知道使用您的语法与 await Task.Factory.StartNew(() => Process.Start 有什么区别,但是谢谢您的建议。
  • 当您执行 await Task.Factory.StartNew 时,它会启动一个新线程并在该线程上执行您的 Process.Start。当它启动一个新线程时,当前线程被释放。如果这是您尝试做的,这很好。由于您的 PrintOrderAsync 方法返回一个字符串任务,因此您必须在调用 PrintOrderAsync 并将结果分配给字符串变量时等待它。
  • M Tovbin,非常感谢您的解释。但是当我更改我的代码“Task result = PrintOrderAsync(PrintExe, ExePath, nOrderNo);”使用您的代码“字符串结果 = await PrintOrderAsync(PrintExe, ExePath, nOrderNo);” VS 显示以下错误:“await 运算符只能在异步方法中使用。请考虑使用 'async' 修饰符将此方法并将其返回类型更改为 'Task'”
  • 在方法中使用 await 时,该方法必须标记为异步。如果它不是一个委托,它也应该返回一个 Task 并且应该在链上一直等待。使用 async/await 时,您需要一直使用它。如果该方法不是委托并且您将其保留为 async void,则您的错误处理将不会像您预期的那样工作。
猜你喜欢
  • 2013-02-10
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
  • 1970-01-01
  • 2014-09-06
  • 2017-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多