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

时间:2016-04-15 17:59:01

标签: c# .net multithreading asynchronous async-await

好的,我希望我得到了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

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

首先打印Main Thread

然后在完成8秒后,Long Task然后New Thread打印出来。

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

public IList<createcaseoutput> createCase(CreateCaseInput CreateCaseInput,SaveCaseSearchInput SaveCaseSearchInput)    
{
    .............
    SQL.CaseSQL.getCreateCaseParameters(CreateCaseInput, out strSPQuery, out listParam);    
    var AcctLst = rep.ExecuteStoredProcedure<createcaseoutput>(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<savecasesearchoutput>> saveCaseSearch(SaveCaseSearchInput SaveCaseSearchInput,Int64? case_key)

{
    ..........................
    SQL.CaseSQL.getSaveCaseSearchParameters(SaveCaseSearchInput, case_key, out strSPQuery, out listParam);

    var AcctLst = await rep.ExecuteStoredProcedureAsync<entities.case.savecasesearchoutput>(strSPQuery, listParam);

    return AcctLst;
}

此处createCase也遇到了等待和 它应立即返回并执行下一行本身并打印Async called 甚至在SaveCaseSearch完成之前呢?

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

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

中,那就好了
async DoWork() {....
}

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

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

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

有时我也会看到

之间的混淆
await someAsync() 

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

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

4 个答案:

答案 0 :(得分:4)

  

我的问题出在我的申请表中:

您的代码无法编译,因为您使用的await没有async。更正后的代码为:

public async Task<IList<createcaseoutput>> 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正在执行一些异步操作,因此它会将不完整的任务返回给createCaseAsynccreateCaseAsync然后await该任务,导致它将一个未完成的任务返回给调用者。

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

  

如果我将调用SavCaseSearch包装在另一个async / await方法

中,那就好了

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

  

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

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

答案 1 :(得分:3)

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

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

您的示例是混合同步和异步的经典案例。 func getFreeSpace() -> CGFloat { do { let fileAttributes = try NSFileManager.defaultManager().attributesOfFileSystemForPath("/") if let size = fileAttributes[NSFileSystemFreeSize] as? CGFloat { return size } } catch { } return 0 } 调用将锁定线程直到完成。您应该使用Sleep,因为它会释放线程,直到延迟完成。

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

(很抱歉没有直接回答您的问题,但线程是一个复杂的主题,如果您尝试直接将其全部放入,您的大脑就可以炒掉。从小处开始。)

答案 2 :(得分:1)

关于async / await和Tasks ...

之间的区别

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

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

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

有关更多信息,请阅读第4章和第34章;异步编程&#34;在书中#34; Writing High-Performance .NET Code&#34;作者:Ben Watson

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

答案 3 :(得分:1)

这里也有createCase正在遇到await并且应该立即返回并执行下一行本身并打印Async之前调用甚至SaveCaseSearch正确完成?

这甚至不应该编译。等待&#39;等待&#39;运算符只能在异步&#39;中使用。方法。也就是说,如果你删除等待&#39;等等。运算符,然后下一行将打印&#34; Async调用&#34;甚至在saveCaseSearch完成之前。

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

saveCaseSearch已经是&#39; async&#39;方法,所以你不需要包装它来达到预期的效果。也就是说,如果你真的想要,可以用另一种方法包装它。

它们之间的区别是什么?和哪一个?

等待&#39; operator等待一个Task对象,这样任何一个都可以。我会选择await someAsync(),因为它编写的代码较少。