问题描述
虽然我在.NET中使用异步代码已有一段时间,但直到最近才开始对其进行研究并了解发生了什么。我刚刚遍历了我的代码并尝试对其进行更改,因此,如果可以同时执行某项任务和某些工作,那就可以了。因此,例如:
Whilst I've been using async code in .NET for a while, I've only recently started to research it and understand what's going on. I've just been going through my code and trying to alter it so if a task can be done in parallel to some work, then it is. So for example:
var user = await _userRepo.GetByUsername(User.Identity.Name);
//Some minor work that doesn't rely on the user object
user = await _userRepo.UpdateLastAccessed(user, DateTime.Now);
return user;
现在变为:
var userTask = _userRepo.GetByUsername(User.Identity.Name);
//Some work that doesn't rely on the user object
user = await _userRepo.UpdateLastAccessed(userTask.Result, DateTime.Now);
return user;
我的理解是,现在正在从数据库中获取用户对象,同时正在进行一些无关的工作。但是,我看到的内容暗示该结果应该很少使用,并且首选使用wait,但我不明白为什么如果我可以在执行其他独立逻辑的情况下还要等待提取用户对象的原因
My understand is that the user object is now being fetched from the database WHILST some unrelated work is going on. However, things I've seen posted imply that result should be used rarely and await is preferred but I don't understand why I'd want to wait for my user object to be fetched if I can be performing some other independant logic at the same time?
推荐答案
让我们确保不要在这里埋藏书架:
Let's make sure to not bury the lede here:
从不
您可以重组控制流以提高性能的直觉是非常正确的。使用 Result
这样做是WRONG WRONG WRONG。
Your instinct that you can restructure your control flow to improve performance is excellent and correct. Using Result
to do so is WRONG WRONG WRONG.
重写代码的正确方法是
var userTask = _userRepo.GetByUsername(User.Identity.Name);
//Some work that doesn't rely on the user object
user = await _userRepo.UpdateLastAccessed(await userTask, DateTime.Now);
return user;
请记住,等待不会使呼叫异步。 等待仅表示如果此任务的结果尚不可用,请执行其他操作,然后在可用后返回此处。调用已已经异步:它将返回任务。
Remember, await does not make a call asynchronous. Await simply means "if the result of this task is not yet available, go do something else and come back here after it is available". The call is already asynchronous: it returns a task.
人们似乎认为 await
具有共同呼叫的语义;它不是。相反,正在等待任务 comonad 上的提取操作;它是 tasks 上的运算符,而不是 call表达式上的运算符。通常,您在方法调用中看到它只是因为它是将异步操作抽象为方法的一种常见模式。 返回的任务是等待的东西,而不是呼叫。
People seem to think that await
has the semantics of a co-call; it does not. Rather, await is the extract operation on the task comonad; it is an operator on tasks, not call expressions. You normally see it on method calls simply because it is a common pattern to abstract away an async operation as a method. The returned task is the thing that is awaited, not the call.
为什么您相信使用 Result
可以让您同时执行其他独立逻辑时间??? 结果阻止您执行此操作。结果是同步等待。您的线程在同步等待任务完成时不能做任何其他工作。使用异步等待可以提高效率。请记住,等待
只是表示在完成此任务之前,该工作流无法继续进行,因此,如果未完成,请找到更多工作要做,稍后再回来。如您所述,过早的
可能导致工作流程效率低下,因为有时即使任务未完成,工作流程仍可以进行。
Why do you believe that using Result
will allow you to perform other independent logic at the same time??? Result prevents you from doing exactly that. Result is a synchronous wait. Your thread cannot be doing any other work while it is synchronously waiting for the task to complete. Use an asynchronous wait to improve efficiency. Remember, await
simply means "this workflow cannot progress further until this task is completed, so if it is not complete, find more work to do and come back later". A too-early await
can, as you note, make for an inefficient workflow because sometimes the workflow can progress even if the task is not complete.
总而言之,在等待发生的地方移动以提高工作流程的效率,但永远不要将其更改为结果
。如果您相信使用 Result
会提高工作流中的并行化效率,您会对异步工作流的工作原理有一些误解。检查您的信念,看看是否可以找出是哪一个给您这种不正确的直觉。
By all means, move around where the awaits happen to improve efficiency of your workflow, but never never never change them into Result
. You have some deep misunderstanding of how asynchronous workflows work if you believe that using Result
will ever improve efficiency of parallelism in the workflow. Examine your beliefs and see if you can figure out which one is giving you this incorrect intuition.
绝对不能使用 Result的原因像这样的code>不仅是因为异步工作流正在进行时同步等待效率低下。 它最终会挂起您的进程。请考虑以下工作流程:
The reason why you must never use Result
like this is not just because it is inefficient to synchronously wait when you have an asynchronous workflow underway. It will eventually hang your process. Consider the following workflow:
-
task1
表示将要执行的作业 - 异步函数Foo等待task1。
- task1尚未出现完成后,Foo返回,从而允许该线程运行更多工作。 Foo返回一个代表其工作流程的任务,并注册完成该任务,作为
task1
的完成。 - 线程现在可以自由执行将来可以使用,包括
task1
。 -
task1
完成后,会触发执行Foo
的工作流的完成,并最终完成代表Foo
的工作流的任务。
task1
represents a job that will be scheduled to execute on this thread in the future and produce a result.- asynchronous function Foo awaits task1.
- task1 is not yet complete, so Foo returns, allowing this thread to run more work. Foo returns a task representing its workflow, and signs up completing that task as the completion of
task1
. - The thread is now free to do work in the future, including
task1
. task1
completes, triggering the execution of the completion of the workflow ofFoo
, and eventually completing the task representing the workflow ofFoo
.
现在假设 Foo
而是获取 Result $ c
task1
中的$ c>。怎么了? Foo
同步等待 task1
完成,这正在等待当前线程可用,因为我们正在同步等待中。如果任务以某种方式关联到当前线程,则调用结果会导致线程与自身死锁。您现在可以使死锁不涉及任何锁,而仅涉及一个线程!不要这样做。
Now suppose Foo
instead fetches Result
of task1
. What happens? Foo
synchronously waits for task1
to complete, which is waiting for the current thread to become available, which never happens because we're in a synchronous wait. Calling Result causes a thread to deadlock with itself if the task is somehow affinitized to the current thread. You can now make deadlocks involving no locks and only one thread! Don't do this.
这篇关于什么时候是使用Task.Result而不是等待Task的最佳位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!