我正在尝试创建一个异步的ProducerConsumerCollection,为此,我正在使用这个msdn页面(http://msdn.microsoft.com/en-us/library/hh873173.aspx(页面底部))。
我现在正在尝试添加超时,这就是我所做的:
public async Task<T> TakeWithTimeout(int timeout)
{
Task<T> takeTask = this.Take();
if (timeout <= 0 || takeTask == await Task.WhenAny(this.tasks.Take(), Task.Delay(timeout)))
{
return await takeTask;
}
else
{
// Timeout
return default(T);
}
}
}
此代码的问题在于,如果超时,它不会取消Take()方法创建的任务。
由于TaskCompletionSource已经“创建”了这个任务,我不能给它一个cancelToken吗?
那么,如何取消它并正确实现此Take with timeout?
谢谢:)
答案 0 :(得分:4)
编写取消安全async
- 友好的生产者/消费者集合并非易事。您需要做的是更改Take
以接受CancellationToken
作为参数,并且应该注册处理程序,以便在取消时TaskCompletionSource
被取消。
我强烈建议您使用内置取消支持的BufferBlock<T>
。
如果您不能使用TPL Dataflow(例如,您在PCL中工作或Dataflow不支持目标平台),那么您可以在我的开源AsyncEx library中使用生产者/消费者集合(例如AsyncProducerConsumerQueue
或AsyncCollection
)。这些都基于AsyncLock
和AsyncConditionVariable
,这是我简要描述的设计on my blog(没有进入取消细节)。使用此设计支持取消生产者/消费者集合的关键是支持AsyncConditionVariable.WaitAsync
中的取消;一旦你的条件变量类型支持取消,那么你的收藏也很容易支持它。
答案 1 :(得分:1)
我只是将我的解决方案发布到如何从TaskCompletionSource取消任务这个问题,因为这就是我自己需要的。
我猜这可以用于您的特定需求,但它不依赖于特定的超时功能,所以这是一个通用的解决方案(或者我希望如此)。
这是一种扩展方法:
public static async Task WaitAsync<T>(this TaskCompletionSource<T> tcs, CancellationToken ctok)
{
CancellationTokenSource cts = null;
CancellationTokenSource linkedCts = null;
try {
cts = new CancellationTokenSource();
linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ctok);
var exitTok = linkedCts.Token;
Func<Task> listenForCancelTaskFnc = async () => {
await Task.Delay(-1, exitTok).ConfigureAwait(false);
};
var cancelTask = listenForCancelTaskFnc();
await Task.WhenAny(new Task[] { tcs.Task, cancelTask }).ConfigureAwait(false);
cts.Cancel();
} finally {
if(linkedCts != null) linkedCts.Dispose();
}
}
用法:
async Task TestAsync(CancellationToken ctok) {
var tcs = new TaskCompletionSource<bool>();
if (somethingOrTheOther) {
tcs.TrySetResult(true);
}
await tcs.WaitAsync(ctok);
}
我们的想法是让一个监督异步任务基本上永远等待它被取消,我们可以用{早期退出'以防TaskCompletionSource
尚未满足,但我们需要退出,无论如何由于取消请求。
保证监督任务在WaitAsync
结束时取消,无论其如何脱离WhenAny
。 TaskCompletionSource
对结果感到满意,并且WhenAny
完成,短暂地离开监督睡眠任务,直到调用cts.Cancel()
的下一行,或它已被exitToken
取消,ctok
是传入cts.Token
或内部Thing
的组合标记。
无论如何,我希望这是有道理的 - 请告诉我这段代码是否有问题......