7

我想使用方法中的 Task return 在稍后可用时返回该值,以便调用者可以使用 Wait 阻止或附加延续,甚至等待它。我能想到的最好的是:

 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 是我正在寻找的东西,我只是让自己的生活变得艰难吗?

4

3 回答 3

11

好吧,我想我应该在发布之前多挖一点。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);
于 2011-05-28T07:23:24.747 回答
3

通过调用 WaitHandle.WaitOne() 来阻塞线程是不好的,但这就是事件的工作方式,并且选择的答案没有多大意义,因为它不会异步执行任何操作。

但是,.NET 框架可以利用线程池中的工作线程同时等待多个事件(请参阅ThreadPool.RegisterWaitForSingleObjec t) - 如果您需要在同时。

因此,您可以做的是注册 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
}
于 2016-02-22T04:47:49.113 回答
0

你不能只是利用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;
}
于 2011-05-27T21:51:05.113 回答