代码之家  ›  专栏  ›  技术社区  ›  kofifus

为什么没有任务。运行接受任务的重载?

c#
  •  -1
  • kofifus  · 技术社区  · 5 年前

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

    ?

    1 回复  |  直到 5 年前
        1
  •  4
  •   Dai    5 年前

    Run

    是的,因为不应该有。

    为了这个答案 Task Task<T> 表示执行某些操作并返回一个值 可能存在,也可能不存在 . 它是对各种工作的抽象(例如,在另一个线程上运行的并发操作、在硬件中的其他地方运行的异步IO操作、同步操作的表示或其他东西)。

    多棒的 任务 / Func<> Action<> .

    Task.Run 具体来说:真实的 Task.Run(Func<>) / Task.Run(Action) 启动 功能<> 行动<> 在默认调度程序的线程池中(即并发、多线程)。你不能“重启”a 任务 (一) 任务 的状态机图是严格单向的),只能启动新的 任务 . 因此不能任意重新启动异步 Socket

    如果你有 任务<T> 对象,则(假设您正确使用了它)它表示的任何操作都将已计划或以其他方式启动-或已完成-因此您不能“启动”一个 任务 把它传给 因为它已经启动了(这是一个过于简单化的过程)。


    你给出的例子(转载如下)没有任何用处:

    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) 方法被调用,它执行以下操作:

    1. 会安排的 LambdaCapture.Run
    2. 然后它将创建并返回一个单独的新 任务 实例来表示线程池操作(即并发操作),而不考虑 originalTask .
    3. ,它会 检查是否 原阿尔塔斯克 已完成,如果是,将返回并通知 原阿尔塔斯克 Runnable.Run (即 await -只是一个 return; 原阿尔塔斯克 延续
    4. 所以当 完成(假设 原阿尔塔斯克 然后运行其余的 操作并通知工作线程池调度程序已完成,然后(可能)执行 等待 awaits 这个 从返回 任务。运行 .
      • 如果这让人困惑,那是因为我不善于解释。 任务<T> 在C#中的工作方式与 Promise<T> std::promise

    简而言之:除了在线程池线程中浪费CPU周期之外,没有理由按照您的建议去做。正如@Fabio所说,只要做 await task

    等待任务 在你原来的方法中,因为那个方法不是 async 方法,即使 仍然 需要 等待 这个 任务 是由 任务。运行