В задаче Параллельная библиотека: как отложить задание. Задача задачи. Fromasync Задача выполнения?
-
26-09-2019 - |
Вопрос
У меня есть метод, который возвращает задачу, как:
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);
}
Я хотел бы сохранить ссылку на задачу и запустить ее позже. Однако кажется, что задача, создаваемая методом PRYLASYNC, выполнена немедленно. Как я могу отложить его исполнение?
Решение
Для начала, если вы посмотрите на синтаксис, вы поймете, что вы на самом деле вызываете BeginSend
метод самостоятельно и заставляя его вернуть IAsyncResult
Для первого параметра peryasync. Это всего лишь одна из перегруженных перегрузок PerrySync. Если вы посмотрите, есть другие перегрузки, и те, которые вы ищете, это те, которые принимают Func<...>
вместо. К сожалению, эти перегрузки также могут вызвать метод сразу от вашего имени из-за того, что в соответствии с охватами то, что действительно происходит, так это то, что fromasync просто обертывает шаблон вызова APM с использованием 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()
Работа не начнется.
Другие советы
Если ваш интерфейс требует, чтобы вы вернули задачу, вы можете в конечном итоге без необходимости планирования работать на пуле резьбы, чтобы сделать вызов начнев () (это то, что происходит в предыдущем ответе, где вызов roysync () завернут другую задачу ).
Вместо этого, если вы можете изменить интерфейс, вы можете использовать стандартную технику исполнения задержки, такие как:
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);
}
Вызывающий абоненты вызовут результат, чтобы запустить операцию (т. Е. Вызов roysync / allendend) и использовать продолжение () для обработки результата:
Func<Task<int>> sendAsync = socket.SendAsync(buffer, offset, count);
sendAsync().ContinueWith(
antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));
Если выставляя функцию функции <> в вашем интерфейсе не подходит, вы можете обернуть его в отдельный класс:
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."));
Обертывание задачи с другой задачей может отложить выполнение.
// 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);