Есть ли способ использовать Task <T>как ручку ожидания для будущего значения T? - PullRequest
7 голосов
/ 28 мая 2011

Я хотел бы использовать возврат метода из метода, чтобы возвратить значение, когда оно станет доступным позднее, чтобы вызывающая сторона могла либо заблокировать, используя Ожидание, либо прикрепить продолжение, либо даже ожидать его.Лучшее, о чем я могу думать, это:

 public class Future<T> {
    private ManualResetEvent mre = new ManualResetEvent();
    private T value;
    public async Task<T> GetTask() {
        mre.WaitOne();
        return value;
    }
    public void Return(T value) {
        this.value = value;
        mre.Set();
    }
}

Основная проблема в том, что mre.WaitOne () блокируется, поэтому я предполагаю, что каждый вызов GetTask () будет планировать новый поток для блокировки.Есть ли способ ожидания WaitHandle в асинхронном режиме или уже есть помощник для создания эквивалентной функциональности?

Редактировать: Хорошо, это TaskCompletionSource, что я ищу, и я просто усложняю жизньна себя?

Ответы [ 3 ]

11 голосов
/ 28 мая 2011

Ну, я думаю, мне надо было еще немного покопаться перед публикацией. TaskCompletionSource это именно то, что я искал

var tcs = new TaskCompletionSource<int>();
bool completed = false;
var tc = tcs.Task.ContinueWith(t => completed = t.IsCompleted);
tcs.SetResult(1);
tc.Wait();
Assert.IsTrue(completed);
3 голосов
/ 22 февраля 2016

Блокировка потока - это плохо, вызывая WaitHandle.WaitOne (), но именно так работают события, и выбранный ответ не имеет большого смысла, потому что он ничего не делает асинхронно.

ОДНАКО, .NET Framework может использовать рабочие потоки из пула потоков для ожидания нескольких событий одновременно (см. ThreadPool.RegisterWaitForSingleObjec t) - это улучшит общее использование ресурсов в вашем приложении, если вам нужно подождать несколько WaitHandles одновременно.

Итак, вы можете зарегистрировать WaitHandle для ожидания в рабочем потоке и установить обратный вызов с желаемым продолжением. С помощью библиотеки расширений AsyncWaitHandle ( NuGet ) это можно сделать в одну строку:

var mre = new ManualResetEvent();
T myValue = ...;
Task<T> futureValueTask = mre.WaitOneAsync().ContinueWith(() => myValue);

В целом, мое скромное предложение - пересмотреть код и сделать вместо этого:

async Task MyCode()
{
  var mre = new ManualResetEvent();
  StartDoingSmthAsynchronouslyThatPulsesTheEvent(mre);
  await mre;
  // finish routine here when the event fires
}
0 голосов
/ 28 мая 2011

Разве вы не можете просто использовать TaskEx.WhenAll(t1, t2, t3...) для ожидания.Вам понадобится реальная задача, которая представляет выполнение работы, но если вы сказали что-то вроде:

private Task<T> myRealWork; 
private T value;
// ...


public async Task<T> GetTask() 
{
    value = await TaskEx.WhenAll(myRealWork); 
    return value;
}

Хотя вы, вероятно, можете просто дождаться задачи myRealWork.Я не вижу в вашем коде, где значение фактически вычисляется.Это может потребовать что-то вроде:

public async Task<T> GetTask() 
{
    value = await TaskEx.RunEx(() => ComputeRealValueAndReturnIt()); 
    return value;
}
...