4

我有一个返回任务的方法,例如:

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 方法创建的任务是立即执行的。我怎样才能推迟它的执行?

4

3 回答 3

5

如果您的界面要求您返回一个任务,那么您最终可能会不必要地在线程池上安排工作,只是为了进行 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."));
于 2011-05-14T22:09:19.420 回答
1

对于初学者,如果您查看语法,您会意识到您实际上是在BeginSend自己调用该方法并使其返回IAsyncResultFromAsync 的第一个参数。不过,这只是 FromAsync 的重载之一。如果您查看还有其他重载,而您正在寻找的是那些Func<...>替代的重载。不幸的是,这些重载也将代表您立即调用该方法,因为在幕后,真正发生的是 FromAsync 只是使用TaskCompletionSource<TResult>.

我实际上可以看到您能够推迟执行的唯一方法是将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(),工作不会开始。

于 2010-10-30T05:09:09.250 回答
0

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

  // 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);
于 2011-12-11T17:33:51.137 回答