【问题标题】:Async/Await VS Task.Run : When to use ? How to use?Async/Await VS Task.Run:何时使用?如何使用?
【发布时间】:2016-08-07 19:50:30
【问题描述】:

好的,我希望我掌握了 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() 遇到 await 时,相应地返回到调用者,即这里的主函数,保存同步上下文并继续从那里执行。

它首先打印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 包装在另一个名为假设的 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(() =&gt; someAsync())..

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

【问题讨论】:

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


【解决方案1】:

我的问题在我的申请中:

您的代码无法编译,因为您使用的是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 也遇到了等待,它应该立即返回并执行下一行本身并打印在 SaveCaseSearch 完成之前调用的 Async 对吗?

没有。这段代码:

  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 然后awaits 那个任务,这导致它返回一个不完整的任务给它的调用者。

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

如果我将调用 SavCaseSearch 包装在另一个 async/await 方法中是不是这样

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

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

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

【讨论】:

    【解决方案2】:

    async 的第一条规则是始终使用 async 或从不使用 async。

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

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

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

    (很抱歉没有直接回答你的问题,但是线程是一个复杂的话题,如果你试图直接把它全部放进去,你的大脑可能会被烧毁。从小处着手。)

    【讨论】:

      【解决方案3】:

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

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

      我的想法是否正确?

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

      它们有什么区别?以及关注哪一个?

      'await' 操作符等待一个 Task 对象,所以任何一个都可以。我会选择await someAsync(),因为它需要编写的代码更少。

      【讨论】:

        【解决方案4】:

        关于 async/await 和 Tasks 的区别...

        Async/Await 是用于简化代码的语法关键字,因为 await 关键字之前的一切都发生在调用线程中,而从 await 开始发生的一切都在任务的继续。

        使用 TPL 对任务进行设置将需要大量代码,并且可读性会受到影响。但是请注意,它的下方仍在使用 Tasks 和 Continuations。

        此外,它们不能总是用来代替任务,例如当 Task 的完成是不确定的,或者如果您有多个级别的任务以及 TaskCompletionSource的使用>.

        有关更多信息,请阅读 Ben Watson 所著“Writing High-Performance .NET Code”一书中的第 4 章“异步编程”

        另请注意,在内部,TPL 使用 .NET 线程池,但这样做更智能,通过在将线程返回池之前按顺序在同一线程上执行多个任务。它可以通过智能使用委托对象来做到这一点。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-05-08
          • 2022-01-07
          • 2020-07-16
          • 1970-01-01
          相关资源
          最近更新 更多