在任务并行库:如何推迟Task.TaskFactory.FromAsync任务执行?

问题描述:

我有一个返回类似任务的方法:在任务并行库:如何推迟Task.TaskFactory.FromAsync任务执行?

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方法创建的任务立即执行。我如何推迟执行?

对于初学者来说,如果你看看语法,你会发现你实际上是在调用BeginSend方法,并导致它返回IAsyncResult作为FromAsync的第一个参数。这只是FromAsync的重载之一。如果你看看还有其他重载,而你正在寻找的是那些取而代之的是Func<...>。不幸的是,这些重载也会以您的名义立即调用该方法,因为在封面之下,真正发生的事情是FromAsync仅使用TaskCompletionSource<TResult>来包装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()工作不会开始。

+1

不幸的是,据我所见,这会导致父任务阻塞等待异步操作结果的线程(在lambda内调用.Result)。如果你必须阻止,你可能只需要调用同步版本! 除非您返回任务>我找不到一种不涉及阻塞的方法,并且可以在稍后启动(连接到TaskCompletionSource的任务不能启动,否则其他各种选择将是可能的) – piers7 2010-12-09 01:59:50

如果你的接口要求您返回一个任务,你可能会不必要地调度线程池的工作只是为了让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.")); 
+0

这应该是被接受的答案。 – caesay 2016-01-18 21:11:37

结束语另一个任务可以推迟执行的任务。

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