Is there a way to use Task<T> as a waithandle for a future value T?
Asked Answered
P

3

7

I'd like to use Task return from a method to return a the value when it becomes available at later time, so that the caller can either block using Wait or attach a continuation or even await it. The best I can think of is this:

 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();
    }
}

Main problem with that is that mre.WaitOne() is blocking, so i assume that every call to GetTask() will schedule a new thread to block. Is there a way to await a WaitHandle in an async manner or is there already a helper for building the equivalent functionality?

Edit: Ok, is TaskCompletionSource what i'm looking for and i'm just making life hard on myself?

Proclaim answered 27/5, 2011 at 21:43 Comment(0)
P
11

Well, I guess I should have dug around a bit more before posting. TaskCompletionSource is exactly what I was looking for

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);
Proclaim answered 28/5, 2011 at 7:23 Comment(1)
I use TaskCompletionSource to solve this problem for me as well - to me it's a much more elegant wait handle because it is more expressive about your original operation. Expressing cancellation or exceptions can be quite useful to cascade to consumers. :)Centipede
E
3

Blocking a thread is bad by calling the WaitHandle.WaitOne(), but that's how events work, and the selected answer does not make a lot of sense, because it does not do anything asynchronously.

HOWEVER, the .NET framework can utilize worker threads from a thread pool to wait on multiple events at the same time (see ThreadPool.RegisterWaitForSingleObject) - this will improve overall resource utilization in your app if you need to wait on multiple WaitHandles at the same time.

So what you can do, is to register the WaitHandle for waiting on a worker thread, and set the callback with desired continuation. With the AsyncWaitHandle extension library (NuGet) this can be done in one line:

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

Overall, my humble suggestion is to review the code and do this instead:

async Task MyCode()
{
  var mre = new ManualResetEvent();
  StartDoingSmthAsynchronouslyThatPulsesTheEvent(mre);
  await mre;
  // finish routine here when the event fires
}
Expeller answered 22/2, 2016 at 4:47 Comment(0)
B
0

Can't you just leverage TaskEx.WhenAll(t1, t2, t3...) to do the waiting. You'd need a real task that represents the doing of the work, but if you said something like:

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


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

Although you can probably just await the myRealWork task as well. I don't see in your code where the value is actually being computed. That might require something like:

public async Task<T> GetTask() 
{
    value = await TaskEx.RunEx(() => ComputeRealValueAndReturnIt()); 
    return value;
}
Bambibambie answered 27/5, 2011 at 21:51 Comment(1)
The problem i'm trying to solve is that i don't have a ComputeRealValueAndReturn, but that some other thread is producing independently of someone asking for the Task that wants the value.Proclaim

© 2022 - 2024 — McMap. All rights reserved.