3

我有一个不等于Task的等待对象(例如,安装了RX的IObservable<T>)。我想创建一个Task,如果提供的CancellationToken被取消,它将最终取消,否则返回等待对象的结果。我想出了下面的代码:如何在观察CancellationToken的同时等待等待对象?

public static Task ObserveTokenAsync(CancellationToken token) 
{ 
    TaskCompletionSource<Unit> tcs = new TaskCompletionSource<Unit>(); 
    token.Register(() => tcs.SetCanceled()); 
    return tcs.Task; 
} 

public static async Task<T> WrapObservableAsync<T>(IObservable<T> obs) 
{ 
    return await obs; 
} 

public static async Task<T> AwaitWhileObservingTokenAsync<T>(IObservable<T> obs, CancellationToken token) 
{ 
    var obsTask = WrapObservableAsync(obs); 
    var tokenTask = ObserveTokenAsync(token); 
    await Task.WhenAny(obsTask, tokenTask).ConfigureAwait(false); 
    token.ThrowIfCancellationRequested(); 
    return obsTask.Result; 
} 

采用这种方法,我将需要重载/重写了最后两个方法为每个awaitable类型,我将使用的。有没有更好的方法来做到这一点?我的猜测是,INotifyCompletion接口可能在某种程度上是有用的。

此外,如果提供的标记不会被取消,那么ObserveTokenAsync方法是否会导致某种资源/内存泄漏?如果是的话,将AwaitWhileObservingTokenAsync方法末尾的TaskCompletionSource设置完成将是解决问题的好方法吗?

+0

你为什么要改变Rx?它通常要强大得多。 – Enigmativity

+0

@Enigmativity我正在处理的问题是由非常复杂的状态描述的。我发现逻辑很难用功能的方式来表达。因此,我主要使用RX进行一些线程间通信,而大部分软件都是以命令式方式编写的。 – tearvisus

+0

所以这是一个XY问题。也许这可能值得你一段时间来描述你实际上想要做的事情。在某些情况下,Rx非常擅长降低代码的复杂性。也许这里可能有一个很好的解决方案。 – Enigmativity

回答

5

使用Rx的TaskObservableExtensions.ToTask扩展方法。它需要一个CancellationToken,并将从observable中取出最后一个元素来完成返回的任务。当CancellationToken被取消时,返回的任务在等待时将抛出OperationCanceledException。如果observable不包含任何元素,则返回的任务将在等待时抛出异常(可能是InvalidOperationException,尽管我不得不查看它)。

+0

这对RX很有用。但在我的问题中,'IObservable '只是一个例子。我正在寻找解决所有问题的解决方案。 – tearvisus

+0

从技术上讲,C#确定什么可以等待,什么不是基于模式。如果使用正确的签名公开'GetAwaiter'方法,并且从那里继续,可以等待一些内容(检查[this](http://blogs.msdn.com/b/pfxteam/archive/2011/01/01/13 /10115642.aspx)了解更多信息)。所以,你所瞄准的任何解决方案都可能与很多“动态”和东西有关。 –

+0

思考更多关于它的信息,你可能也不会有太大的动态,因为所有可以等待的东西都会有它自己的取消概念。对于observables,它是取消订阅。对于别的东西,如果有的话,取消可以通过其他方式实现。看看你的等待对象是什么样子以及它来自哪里将会很有趣。 –