【问题标题】:Low performance on TaskCanceledExceptionTaskCanceledException 性能低下
【发布时间】:2019-08-16 20:29:48
【问题描述】:

我需要并行运行许多 (10-100k) 转换操作。在这种情况下,我使用tasksSymaphoreSlim 来控制并行度,并使用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


【解决方案1】:

当您的代码执行_semaphore.WaitAsync(_cts.Token) 时,信号量必须设置一个可取消的等待;这意味着它必须向取消令牌注册,并在收到信号时从等待队列中删除等待。所以是的,我预计 100k 注册会出现性能问题。

如果你不需要取消等待,我不会。

await _semaphore.WaitAsync().ConfigureAwait(false);
_cts.Token.ThrowIfCancellationRequested();

【讨论】:

  • 谢谢,@斯蒂芬!即使我尝试直接抛出异常也是一样的。在 Release 构建中它需要很长时间(大约比使用布尔令牌长 10 倍),但它比在 Debug 中快得多。
猜你喜欢
  • 2011-06-15
  • 2011-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多