【发布时间】:2019-08-16 20:29:48
【问题描述】:
我需要并行运行许多 (10-100k) 转换操作。在这种情况下,我使用tasks、SymaphoreSlim 来控制并行度,并使用CancellationTokenSource 来防止在抛出异常时未运行的任务运行。
我正在尝试做这样的事情:
var tasks = table.Body.Select(async row =>
{
await _semaphore.WaitAsync(_cts.Token).ConfigureAwait(false);
try
{
await Convert(row, preparedData, table, mapper).ConfigureAwait(false);
}
catch (Exception)
{
_cts.Cancel();
throw;
}
});
await Task.WhenAll(tasks).ConfigureAwait(false);
但是性能很糟糕。如果我不再使用令牌,只需几秒钟。
var tasks = table.Body.Select(async row =>
{
if (_canceled)
return;
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
await Convert(row, preparedData, table, mapper).ConfigureAwait(false);
}
catch (Exception)
{
_canceled = true;
throw;
}
});
await Task.WhenAll(tasks).ConfigureAwait(false);
有没有办法使用CancellationTokens 进行常规性能?
【问题讨论】:
-
Convert抛出异常有多普遍?如果没有抛出异常,我希望这两者具有相似的性能。毕竟WaitAsync()只是调用WaitAsync(Timeout.Infinite, default(CancellationToken))。WaitAsync也有一个快捷方式,CancellationToken已被取消。 -
table是什么数据类型?你能告诉我们Convert是做什么的吗? -
另外,在您的第二个示例中,
_canceled在获取信号量之前在方法的最顶部进行检查。因此,我希望每行的Select调用在他们中的任何一个有机会获得调用Convert的锁之前检查_canceled,所以我认为取消可能在这里起作用,正如你所描述的那样。 -
@AndreyAlonzov
Convert做什么?为什么不使用专门为数据并行构建的Parallel.ForEach或 PLINQ?并行处理是一回事。并发执行有些不同。 -
@AndreyAlonzov 你还没有发布任何关于
Convert的信息。这确实很重要。如果您不将取消令牌传递给它们,则无法取消正在运行的Convert方法。问题不在于取消令牌。Convert本身会忽略取消,无论您使用什么库,都会继续忽略它
标签: c# async-await task cancellation cancellationtokensource