简体   繁体   English

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

[英]Async/Await VS Task.Run : When to use ? How to use?

Okay I hope I got the basics of async/await but still some questions a re lingering in my head. 好吧,我希望我得到了async / await的基础知识,但仍有一些问题在脑海里挥之不去。

But now it is the problem I am talking about . 但现在我正在谈论的问题。 Suppose in this simple example 假设在这个简单的例子中

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");

}

The main thread still continues and prints Main Thread after calling Method() and encountering an await for 8 seconds . 主线程仍然继续并在调用Method()并遇到等待8秒后打印Main Thread

So accordingly the Method() returns to the caller ie to the main function here once it encounters await , saves the synch context and keeps on executing from there . 因此,当遇到await时,Method()返回调用者,即返回到main函数,保存同步上下文并从那里继续执行。

It prints Main Thread first . 它首先打印Main Thread

Then after 8 seconds complete , Long Task and then New Thread get printed. 然后在8秒完成后,打印Long Task ,然后打印New Thread

This part I got . 这部分我得到了。 My question is in my application : 我的问题在于我的申请:

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;
}

Here also createCase is encountering await and it should return immediately right and execute the very next line itself and print Async called before even the SaveCaseSearch completes right ? 这里也有createCase遇到等待它应该立即返回并执行下一行本身并打印Async called之前Async called甚至SaveCaseSearch完成对吗?

Okay if I am thinking loud it might be control returns to the caller . 好吧,如果我在大声思考它可能会control returns to the caller

So is it like if I wrap my call SavCaseSearch inside another async/await method named suppose 所以如果我将我的调用SavCaseSearch包装在另一个名为suppose的async / await方法中

async DoWork() {....
}

and call this DoWork() from CreateCase() directly so then 然后直接从CreateCase()调用此DoWork()

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

Am I thinking in the correct way ? 我是以正确的方式思考的吗?

Also sometimes I see and get confused between 有时我也会看到并混淆

await someAsync() 

and

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

what's the difference between them ? 他们之间有什么区别? and which one to follow ? 以及哪一个?

My question is in my application : 我的问题在于我的申请:

Your code won't compile because you're using await without async . 您的代码将无法编译,因为您正在使用await而不使用async Corrected code would be: 更正后的代码是:

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;    
}

Here also createCase is encountering await and it should return immediately right and execute the very next line itself and print Async called before even the SaveCaseSearch completes right ? 这里也有createCase遇到等待它应该立即返回并执行下一行本身并打印Async之前调用甚至SaveCaseSearch完成对吗?

No. This code: 不。这段代码:

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

is the same as this code: 与此代码相同:

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

So, first, createCaseAsync would call saveCaseSearchAsync . 因此,首先, createCaseAsync将调用saveCaseSearchAsync Presumably, saveCaseSearchAsync is performing some asynchronous operation, so it will return an incomplete task to createCaseAsync . 据推测, saveCaseSearchAsync正在执行一些异步操作,因此它将向createCaseAsync返回一个不完整的任务。 createCaseAsync then await s that task, which causes it to return an incomplete task to its caller. 然后, createCaseAsync await该任务,这会导致它将未完成的任务返回给调用者。

Eventually, saveCaseSearchAsync will complete, which will complete the task it returned (which I called saveTask in the code above). 最终, saveCaseSearchAsync将完成,这将完成它返回的任务(我在上面的代码中称为saveTask )。 This in turn will continue executing createCaseAsync , and it will proceed to the next line and print "Async called" on the console. 这反过来将继续执行createCaseAsync ,它将继续执行下一行并在控制台上打印“Async called”。

So is it like if I wrap my call SavCaseSearch inside another async/await method 如果我将我的调用SavCaseSearch包装在另一个async / await方法中,那就好了

You wouldn't need a wrapper because createCaseAsync is already returning a Task . 您不需要包装器,因为createCaseAsync已经返回一个Task

what's the difference between them ? 他们之间有什么区别? and which one to follow ? 以及哪一个?

Task.Run is mainly for pushing blocking work off the UI thread and onto the threadpool. Task.Run主要用于将阻塞工作从UI线程推送到线程Task.Run Since you're on ASP.NET, don't use Task.Run . 由于您使用的是ASP.NET,因此请勿使用Task.Run

The first rule of async is to always use async or never use async. 异步的第一个规则是始终使用异步或永远不使用异步。

If your underlying API can't handle async it's no use to use async in the upper layers (like ASP.NET MVC), since at some point you'll get thread starvation as all threads are occupied waiting on IO operations (like DB calls). 如果您的底层API无法处理异步,那么在上层(如ASP.NET MVC)中使用async是没有用的,因为在某些时候,由于所有线程都在等待IO操作(如DB调用),因此您将获得线程饥饿)。

Your example is a classical case where you mix sync and async. 您的示例是混合同步和异步的经典案例。 The Sleep call will lock the thread until it completes. Sleep调用将锁定线程直到完成。 You should have used Task.Delay instead as it would have released the thread until the delay completes. 你应该使用Task.Delay因为它会释放线程,直到延迟完成。

My advice to you is to simply start by following the rule I mentioned first and only when IO bound operations like DB or file calls are involved. 我的建议是简单地按照我先提到的规则开始,并且仅在涉及数据库或文件调用等IO绑定操作时才开始。 Then when you understand async better you can start to break it since you then have a much better understanding in what it can lead to. 然后当你更好地理解异步时,你可以开始打破它,因为你可以更好地理解它可以导致什么。

(Sorry for not answering your questions directly, but threading is a complex topic and your brain can fry if you try to get it all in directly. Start small.) (很抱歉没有直接回答你的问题,但是线程是一个复杂的话题,如果你想直接把它全部搞砸,你的大脑就会炒掉。从小处开始。)

Regarding the difference between async/await and Tasks... 关于async / await和Tasks之间的区别......

Async/Await are syntactic keywords to simplify your code, as everything before the await keyword happens in the calling thread and everything from the await onward happens is in the task's continuation. Async / Await是用于简化代码的语法关键字,因为await关键字之前的所有内容都发生在调用线程中,而等待之后的所有内容都发生在任务的延续中。

Setting this up with tasks using the TPL will require a lot of code and readability suffers. 使用TPL对任务进行设置将需要大量代码和可读性。 Note however, Underneath it is still using Tasks and Continuations. 但请注意,其下方仍在使用“任务”和“续集”。

Further, they cannot always be used in place of Tasks like when the completion of Task is nondeterministic, or if you have multiple levels of Tasks along with the usage of TaskCompletionSource . 此外,它们不能总是用来代替任务,例如当Task的完成不确定时,或者如果你有多个任务级别以及TaskCompletionSource的使用。

For more information read the chapter 4 "Asynchronous Programming" in the book " Writing High-Performance .NET Code " by Ben Watson 有关更多信息,请阅读Ben Watson 撰写的“ 编写高性能.NET代码 ”一书中的第4章“异步编程”

Note also, Internally, the TPL uses the .NET thread pool, but does so more intelligently, by executing multiple Tasks on the same thread sequentially before returning the thread back to the pool. 另请注意,在内部,TPL使用.NET线程池,但通过在将线程返回池之前依次在同一线程上执行多个Tasks来更智能地执行。 It can do this via intelligent use of delegate objects. 它可以通过智能使用委托对象来实现。

Here also createCase is encountering await and it should return immediately right and execute the very next line itself and print Async called before even the SaveCaseSearch completes right ? 这里也有createCase遇到等待它应该立即返回并执行下一行本身并打印Async之前调用甚至SaveCaseSearch完成对吗?

This shouldn't even compile. 这甚至不应该编译。 The 'await' operator can only be use within an 'async' method. 'await'运算符只能在'async'方法中使用。 That said, if you remove the 'await' operator, then the next line will print "Async called" before even the saveCaseSearch completes. 也就是说,如果删除'await'运算符,那么即使saveCaseSearch完成,下一行也将打印“Async called”。

Am I thinking in the correct way ? 我是以正确的方式思考的吗?

saveCaseSearch is already an 'async' method, so you don't need to wrap it to achieve the desired result. saveCaseSearch已经是一个“异步”方法,因此您无需将其包装以获得所需的结果。 That said, you could wrap it in another method, if you really want to. 也就是说,如果你真的想要,可以用另一种方法包装它。

what's the difference between them ? 他们之间有什么区别? and which one to follow ? 以及哪一个?

The 'await' operator wait for a Task object so either one is fine. 'await'运算符等待Task对象,因此任何一个都可以。 I would chose await someAsync() , because it's less code to write. 我会选择await someAsync() ,因为它编写的代码较少。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM