Есть ли способ использовать Task в качестве waithandle для будущего значения T?

Я хотел бы использовать Task return из метода, чтобы вернуть значение, когда оно станет доступным позже, чтобы вызывающий объект мог либо блокировать с помощью Wait, либо присоединить продолжение или даже дождаться его. Лучшее, что я могу придумать, это:--2-->

 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 асинхронным способом или уже есть помощник для создание эквивалентной функциональности?

Edit: хорошо, Является ли TaskCompletionSource тем, что я ищу, и я просто усложняю себе жизнь?

3 ответов


Ну, я думаю, я должен был покопаться немного больше перед публикацией. 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);

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

однако .NET framework может использовать рабочие потоки из пула потоков для одновременного ожидания нескольких событий (см. класса ThreadPool.RegisterWaitForSingleObject) - это улучшит общее использование ресурсов в вашем приложении, если вам нужно подождать несколько Объекты waithandle в то же время.

Итак, что вы можете сделать, это зарегистрировать 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
}

вы не можете просто использовать 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;
}