В библиотеке параллельных задач: Как отложить выполнение задачи Task.TaskFactory.FromAsync? - PullRequest
4 голосов
/ 27 октября 2010

У меня есть метод, который возвращает задачу, например:

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, выполняется немедленно.Как я могу отложить его выполнение?

Ответы [ 3 ]

5 голосов
/ 15 мая 2011

Если ваш интерфейс требует, чтобы вы вернули задачу, вы можете закончить ненужное планирование работы в пуле потоков, просто чтобы выполнить вызов 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."));
1 голос
/ 30 октября 2010

Для начала, если вы посмотрите на синтаксис, вы поймете, что на самом деле вы сами вызываете метод BeginSend и заставляете его возвращать IAsyncResult для первого параметра FromAsync. Это только одна из перегрузок FromAsync, хотя. Если вы посмотрите, есть и другие перегрузки, и те, которые вам нужны, вместо них принимают Func<...>. К сожалению, эти перегрузки также сразу вызовут метод от вашего имени, потому что под прикрытием действительно происходит то, что FromAsync просто оборачивает шаблон вызова APM, используя TaskCompletionSource<TResult>.

Единственный способ увидеть, что вы можете отложить выполнение, - это обернуть работу 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 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(), работа не начнется.

0 голосов
/ 11 декабря 2011

Упаковка задачи с другой задачей может отложить выполнение.

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