我有以下代码,它充当异步消费者:
private readonly Object sync;
private async void ConsumeAsync()
{
// The reason why I have an await here is to yield the control to the caller, e.g: if an exception happens inside the Listen method, it's being propagated and can be handled in caller codes.
await Task.Run(() => this.Listen());
}
private void Listen()
{
...
while (true)
{
lock (sync)
{
while (<blocking-condition>)
{
Monitor.Wait(sync);
}
}
// Consuming happens here
}
}
// Changing the blocking condition happens outside the infinite loop with proper Monitor.Pulse usage
我的设计问题:
- 我有一个异步空洞即发即弃的方法,如果没有消耗,则必须阻止它。这是一种正确的方法吗?
- 我是否必须对任务使用"长时间运行"选项?
- 我必须使用取消令牌吗?
- 我是否正确地将异常传播到更高级别的异常处理程序?
你的四个问题中的两个对于这个论坛来说太宽泛了。只有您可以决定这是否是适合你的方案的"正确方法"。同样,是否使用CancellationToken
的问题取决于您需要哪些功能。如果您需要能够取消操作,这可能是一个好方法,但不是唯一的方法,这里没有足够的细节来了解什么是最好的。你也无法提供足够的细节,同时为这个论坛保持问题足够狭窄。
当然,如果您不需要能够取消操作,则该问题的答案是"不,您不需要使用CancellationToken
":)。
至于另外两个问题,只有一个有明确的答案:
- "我是否必须对任务使用长时间运行选项?"
不,您不必这样做。但强烈建议用于实际上长时间运行的任务。这将有助于任务计划框架正确管理任务的线程。
- "我是否将异常正确传播到更高级别的异常处理程序?"
你是否值得怀疑。由于您的ConsumeAsync()
方法是async void
,您显然不能等待它。做这种事情的自然方法是编写更像这样的代码:
private Task ConsumeAsync()
{
return Task.Run(() => this.Listen());
}
async Task SomeMethodSomewhereElse()
{
try
{
await ConsumeAsync();
}
catch (...)
{
// An exception thrown from your long-running task will be caught here
}
}
请注意,ConsumeAsync()
方法本身实际上并不需要async
。您可以只返回已创建的Task
,并且将完全相同地工作(嗯,更有效......但最主要的只是不要添加完全不必要的额外代码)。
按照你编写它的方式,你必须做很多额外的工作来捕获抛出的异常(即直接从SynchronizationContext
处理它)。也许你做到了,也许你没有。你没有显示该代码,所以我假设你没有。