Async / Await VS Task.Run:何时使用? 如何使用?

好吧,我希望我得到了async / await的基础知识,但仍有一些问题在脑海中挥之不去。

但现在我正在谈论的问题。 假设在这个简单的例子中

static void Main(string[] args) { Method(); Console.WriteLine("Main Thread"); Console.ReadLine(); } public async static void Method() { await Task.Run(new Action(LongTask)); Console.WriteLine("New Thread"); } public static void LongTask() { Thread.Sleep(8000); Console.WriteLine("Long Task"); } 

主线程仍然继续并在调用Method()并遇到等待8秒后打印Main Thread

因此,当遇到await时,Method()返回调用者,即返回到main函数,保存同步上下文并从那里继续执行。

它首先打印Main Thread

然后在8秒完成后, Long TaskNew Thread得到打印。

这部分我得到了。 我的问题在于我的申请:

 public IList createCase(CreateCaseInput CreateCaseInput,SaveCaseSearchInput SaveCaseSearchInput) { ............. SQL.CaseSQL.getCreateCaseParameters(CreateCaseInput, out strSPQuery, out listParam); var AcctLst = rep.ExecuteStoredProcedure(strSPQuery, listParam).ToList(); if (!string.IsNullOrEmpty(AcctLst.ElementAt(0).o_case_seq.ToString())) { await saveCaseSearch(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq); } console.writeline("Async called"); return AcctLst; } public async Task<ilist> saveCaseSearch(SaveCaseSearchInput SaveCaseSearchInput,Int64? case_key) { .......................... SQL.CaseSQL.getSaveCaseSearchParameters(SaveCaseSearchInput, case_key, out strSPQuery, out listParam); var AcctLst = await rep.ExecuteStoredProcedureAsync(strSPQuery, listParam); return AcctLst; } 

这里也有createCase遇到等待,它应该立即返回并执行下一行本身并打印Async called之前Async called甚至SaveCaseSearch完成对吗?

好吧,如果我在大声思考它可能是control returns to the caller

所以如果我将我的调用SavCaseSearch包装在另一个名为suppose的async / await方法中

 async DoWork() {.... } 

然后直接从CreateCase()调用此DoWork()

It will go on printing "Async called" once call to DoWork() encounters await and before it even completes ?

我是以正确的方式思考的吗?

有时我也会看到并感到困惑

 await someAsync() 

await Task.Run(() => someAsync()) ..

它们之间有什么区别? 以及哪一个?

我的问题在于我的申请:

您的代码将无法编译,因为您正在使用await而不使用async 。 更正后的代码是:

 public async Task> createCaseAsync(CreateCaseInput CreateCaseInput,SaveCaseSearchInput SaveCaseSearchInput) { ... await saveCaseSearch(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq); console.writeline("Async called"); return AcctLst; } 

这里也有createCase遇到等待,它应该立即返回并执行下一行本身并打印Async之前调用甚至SaveCaseSearch完成对吗?

不。这段代码:

  await saveCaseSearch(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq); 

与此代码相同:

  var saveTask = saveCaseSearchAsync(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq); await saveTask; 

因此,首先, createCaseAsync将调用saveCaseSearchAsync 。 据推测, saveCaseSearchAsync正在执行一些异步操作,因此它将向createCaseAsync返回一个不完整的任务。 然后, createCaseAsync await该任务,这会导致它将未完成的任务返回给调用者。

最终, saveCaseSearchAsync将完成,这将完成它返回的任务(我在上面的代码中称为saveTask )。 这反过来将继续执行createCaseAsync ,它将继续执行下一行并在控制台上打印“Async called”。

如果我将调用SavCaseSearch包装在另一个async / await方法中,那就好了

您不需要包装器,因为createCaseAsync已经返回一个Task

它们之间有什么区别? 以及哪一个?

Task.Run主要用于将阻塞工作从UI线程推送到线程Task.Run 。 由于您使用的是ASP.NET,因此请勿使用Task.Run

异步的第一个规则是始终使用异步或永远不使用异步。

如果你的底层API无法处理异步,那么在上层使用async是没有用的(比如ASP.NET MVC),因为在某些时候你会得到线程饥饿,因为所有线程都在等待IO操作(比如DB调用) )。

您的示例是混合同步和异步的经典案例。 Sleep调用将锁定线程直到完成。 你应该使用Task.Delay因为它会释放线程,直到延迟完成。

我的建议是简单地开始遵循我之前提到的规则,并且仅在涉及到DB绑定操作(如DB或文件调用)时才开始。 然后当你更好地理解异步时,你可以开始打破它,因为你可以更好地理解它可以导致什么。

(很抱歉没有直接回答你的问题,但是线程是一个复杂的话题,如果你想直接把它全部搞砸,你的大脑就会炒掉。从小处开始。)

这里也有createCase遇到等待,它应该立即返回并执行下一行本身并打印Async之前调用甚至SaveCaseSearch完成对吗?

这甚至不应该编译。 ‘await’运算符只能在’async’方法中使用。 也就是说,如果删除’await’运算符,那么即使saveCaseSearch完成,下一行也会打印“Async called”。

我是以正确的方式思考的吗?

saveCaseSearch已经是一个“异步”方法,因此您无需将其包装以获得所需的结果。 也就是说,如果你真的想要,你可以用另一种方法包装它。

它们之间有什么区别? 以及哪一个?

‘await’运算符等待一个Task对象,所以任何一个都没问题。 我会选择await someAsync() ,因为它写的代码较少。

关于async / await和Tasks之间的区别……

Async / Await是用于简化代码的语法关键字,因为await关键字之前的所有内容都发生在调用线程中,而等待之后的所有内容都发生在任务的延续中。

使用TPL对任务进行设置将需要大量代码和可读性。 但请注意,其下方仍在使用“任务”和“续集”。

此外,它们不能总是用来代替任务,比如当Task的完成不确定时,或者你有多个任务级别以及TaskCompletionSource的使用。

有关更多信息,请阅读Ben Watson 撰写的“ 编写高性能.NET代码 ”一书中的第4章“异步编程”

另请注意,在内部,TPL使用.NET线程池,但通过在将线程返回池之前依次在同一线程上执行多个任务,可以更智能地执行此操作。 它可以通过智能使用委托对象来实现。