【问题标题】:In Task Parallel Library: How to defer Task.TaskFactory.FromAsync task execution?在任务并行库中:如何推迟 Task.TaskFactory.FromAsync 任务执行?
【发布时间】:2010-10-27 08:48:47
【问题描述】:

我有一个方法可以返回如下任务:

public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
    if (socket == null) throw new ArgumentNullException("socket");
    if (buffer == null) throw new ArgumentNullException("buffer");

    return Task.Factory.FromAsync<int>(
        socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
        socket.EndSend);
}

我想保留对任务的引用并稍后运行它。但是,似乎由 FromAsync 方法创建的任务是立即执行的。我怎样才能推迟它的执行?

【问题讨论】:

    标签: .net task-parallel-library


    【解决方案1】:

    如果您的界面要求您返回一个任务,您最终可能会为了进行 BeginSend() 调用而不必要地在线程池上调度工作(这就是在包装 FromAsync() 调用的上一个答案中发生的情况由另一个任务)。

    相反,如果您能够更改接口,则可以使用标准延迟执行技术,例如:

    public static Func<Task<int>> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
    {
        if (socket == null) throw new ArgumentNullException("socket");
        if (buffer == null) throw new ArgumentNullException("buffer");
        return () => 
            Task.Factory.FromAsync<int>(
                socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
                socket.EndSend);
    }
    

    调用者将调用结果来启动操作(即调用 FromAsync/BeginSend)并使用 ContinueWith() 来处理结果:

    Func<Task<int>> sendAsync = socket.SendAsync(buffer, offset, count);
    sendAsync().ContinueWith(
        antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));
    

    如果在你的界面中暴露 Func 是不合适的,你可以将它包装到一个单独的类中:

    public class DelayedTask<TResult>
    {
        private readonly Func<Task<TResult>> func;
    
        public DelayedTask(Func<Task<TResult>> func)
        {
            this.func = func;
        }
    
        public Task<TResult> Start()
        {
            return this.func();
        }
    }
    
    public static DelayedTask<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
    {
        if (socket == null) throw new ArgumentNullException("socket");
        if (buffer == null) throw new ArgumentNullException("buffer");
        return new DelayedTask<int>(() =>
            Task.Factory.FromAsync<int>(
                socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
                socket.EndSend));
    }
    

    调用者看起来像:

    DelayedTask<int> task = socket.SendAsync(buffer, offset, count);
    task.Start().ContinueWith(
        antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));
    

    【讨论】:

      【解决方案2】:

      首先,如果您查看语法,您会意识到您实际上是在自己调用BeginSend 方法并导致它为FromAsync 的第一个参数返回IAsyncResult。不过,这只是 FromAsync 的重载之一。如果您查看还有其他重载,而您正在寻找的是那些采用Func&lt;...&gt; 的重载。不幸的是,这些重载也将代表您立即调用该方法,因为在幕后,真正发生的是 FromAsync 只是使用 TaskCompletionSource&lt;TResult&gt; 包装 APM 调用模式。

      我实际上可以看到您能够推迟执行的唯一方法是将FromAsync 工作实际包装在您自己不Start 的父任务中。例如:

      public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
      {
          if (socket == null) throw new ArgumentNullException("socket");
          if (buffer == null) throw new ArgumentNullException("buffer");
      
          return new Task<int>(() =>
          {
              return Task.Factory.FromAsync<int>(
                               socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
                               socket.EndSend).Result;
          }
      }
      

      现在调用者可以像这样得到任务:

      Task<int> task = SendAsync(...);
      

      在他们致电task.Start() 之前,工作不会开始。

      【讨论】:

      • 不幸的是,据我所知,这会导致父任务阻塞等待异步操作结果完成的线程(在 lambda 中调用 .Result)。如果非要阻塞,不如直接调用同步版本!除非您返回 Task>,否则我想不出一种不涉及阻塞的方法,并且以后仍然可以启动(附加到 TaskCompletionSource 的任务不能启动) ed,否则各种其他选项都是可能的)
      【解决方案3】:

      用另一个任务包装任务可以推迟执行。

        // Wrap the task
        var myTask = new Task<Task<int>>(() => SendAsync(...));
      
        // Defered start
        Thread.Sleep(1000); 
        myTask.Start();
        // Thread is not blocked
        Console.WriteLine("Started");
      
        // Wait for the result
        Console.WriteLine(myTask.Unwrap().Result);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-14
        • 1970-01-01
        • 2015-08-23
        • 1970-01-01
        • 2018-06-08
        • 1970-01-01
        相关资源
        最近更新 更多