为什么没有Task.Run重载接受任务?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么没有Task.Run重载接受任务?相关的知识,希望对你有一定的参考价值。
我发现自己反复这样做:
var task = f(); // some code returning a task
Task.Run(async () => await task);
对我来说,这是我经常迷惑的代码。是否有原因为什么 Task
类没有接受任务的重载?
即
public static Task Run(Task task) => Task.Run(async () => await task);
?
答案
为什么Task类没有接受任务的
Run
重载?
是,因为不应该有一个。
为了这个答案,Task
或Task<T>
表示执行某些工作并返回可能存在或可能不存在的值的某些操作。它是对各种工作的抽象(例如,在另一个线程上运行的并发操作,在硬件中其他地方运行的异步IO操作,同步操作的表示或其他形式。
Task
/ Task<T>
代表什么[[不是是Func<>
或Action<>
,也不代表可以用来启动新操作的“作业模板”(将其视为代表已经开始的工作)。
Task.Run
:在.NET中,真正的Task.Run(Func<>)
/ Task.Run(Action)
方法是默认调度程序的线程池中的Func<>
或Action<>
的简写形式(即,并发,多-线程)。您无法“重新启动” Task
(Task
的状态机图严格是单向的),只能使用用于启动原始Task
的任何机制来启动新的Task
。因此,例如,您不能任意重启异步Socket
操作,因为这将意味着倒退整个程序的状态,并且这意味着违反物理定律...如果您有一个Task<T>
对象,那么(假设您使用的是正确的)对象所代表的任何操作将已经被调度或以其他方式启动-或已经完成-因此,您不能通过以下方式“启动” Task
将其传递给Task.Run
,因为它已经启动了(这过于简化)。
您提供的示例(在下面重新发布)没有任何用处:
public static Task Run(Task task) => Task.Run(async () => await task);
为了方便阅读,我在下面用长篇文章重写了它:
public static Task Run(Task originalTask) LambdaCapture capture = new LambdaCapture( originalTask ); Task poolTask = Run( capture.Run ); // Remember that a Delegate includes the `this` reference unlike a raw C-style function-pointer. return poolTask; // Oversimplified representation of what Task.Run does: public static Task Run( Action action ) ThreadPool pool = GetThreadPoolFromSomewhere(); TaskCompletionSource tsc = new TaskCompletionSource(); Action wrappedAction = () => // Run the action: action(); // When it completes, inform TaskCompletionSource: tsc.SetResult(); // Task (not `Task<T>`) has no result value. // When `SetResult()` is invoked, the thread running this code will not return to here until after it runs the contination scheduled after `originalTask`. ; pool.AddJob( wrappedAction ); // Adds `wrappedAction` to a queue which is dequeued by the first available thread. return tsc.Task; // <-- this is a new Task created by the TaskCompletionSource. private class LambdaCapture private readonly Task originalTask; public Runnable( Task originalTask ) this.originalTask = originalTask; public async Task Run() await this.originalTask;
当您建议的
Task.Run(Task)
方法被调用时,它会执行此操作:
它将安排
LambdaCapture.Run
在线程池中的第一个可用线程上运行。然后它将创建并返回一个单独的新
Task
实例,以表示线程池操作(即并发操作),而不考虑originalTask
的性质。- [当线程池可用并且工作线程运行
LambdaCapture.Run
时,它将- (过度简化警告)
检查originalTask
是否完成,如果完成,将返回并通知originalTask
' s完成的调度程序,如果没有完成,它将调度Runnable.Run
的其余部分(即await
之后的所有代码-在这种情况下只是一个return;
语句)在将其设为- continuation。因此,当
完成)时,从originalTask
确实完成(假设它[[doesoriginalTask
分配下一个延续的线程将运行originalTask
操作的其余部分,并通知工作线程池调度程序完成,然后(大概)从Task.Run
返回的任何代码await
和awaits
执行Task
的延续。- 如果这令人困惑,那是因为我很想解释。 C#中的
简而言之:除了在线程池线程中浪费CPU周期外,没有任何理由要做您想做的事情。如@Fabio所说,只需使用原始方法执行Task.Run
与javascript / TypeScript中的Task<T>
或C ++中的Promise<T>
基本相同。std::promise
。
- 如果您的原始方法不是
await task
,因为该方法不是await task
方法,那么即使async
存在,它也无济于事,因为您[[still需要Task.Run(Task)
await
返回的Task
。以上是关于为什么没有Task.Run重载接受任务?的主要内容,如果未能解决你的问题,请参考以下文章
使用异步方法等待 Task.Run 不会在正确的线程上引发异常
Parallel.ForEach 与 Task.Run 和 Task.WhenAll