Как стать автором
Обновить

Комментарии 13

Рекомендации

Не совсем соглашусь в плане данных рекомендаций. В приведённом вами фрагменте кода происходит утечка регистраций. Вызов tokenRegistration.Dispose() осуществляется только в случае исключения. Если же исключения нет, .Dispose() никем не вызывается.


С тем же успехом можно для каждой итерации создавать свой CancellationTokenSource и забывать делать ему Dispose, когда токен становится не нужен.


То есть код достаточно переписать таким образом, чтобы tokenRegistration.Dispose() вызывался после вызова DbDataReader.Dispose(). Ну да, придётся сделать враппер.


И ещё: проверять cancellationToken.CanBeCanceled нет необходимости, т.к.:
https://github.com/microsoft/referencesource/blob/master/mscorlib/system/threading/CancellationToken.cs#L332


То есть сразу писать:


var tokenRegistration = cancellationToken.Register(new Action(this.CancelIgnoreFailure));
Если мы говорим про пример с исключением, то это оракловый клиент так написан.

Если говорит о нашей регистрации, то Dispose вызывался при остановке сервиса. Но из-за особенностей «примера», о которых мы не знали, происходила утечка регистраций.
Если мы говорим про пример с исключением, то это оракловый клиент так написан.

Ну то есть утечка в оракловом клиенте. Вы же просто написали простой workaround. Такой вариант имеет право на жизнь.

Правда, становится непонятно, в чём все-таки виноват CancellationTokenSource.

Статья, по сути, блог-пост о поиске бага в сторонней библиотеке.

Как оказалось — да. И как следствие: разобрались как все устроено внутри самого CancellationTokenSource.
using (var source = new CancellationTokenSource(TimeSpan.FromSeconds(15))
using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(source.Token, cancellationToken))

Что думаете насчет такого подхода?
к нему мы в итоге и пришли, когда поняли, в чем беда.
А если очень нужно отказаться от п. 1, то используйте для каждой итерации свой CancellationTokenSource и связывайте его с основным через CreateLinkedTokenSource.
То есть клиент оказался абсолютно синхронным (вот это открытие).
Неуправляемый Oracle Client — полностью синхронный внутри.

Управляемый тоже. На форуме Oracle менеджеры по общественному питанию кормят аудиторию завтраками уже который квартал.


Но я считаю, что это ошибка Microsoft. Когда они добавляли асинхронные методы в базовые классы ADO.NET, у них было три варианта


  1. сделать абстрактные методы и сломать все драйвера (Microsoft так не делает никогда)
  2. сделать методы, которые делают throw new NotImplementedException();
  3. сделать методы, которые вызывают синхронные методы и возвращают Task.FromResult()

Что они сделали, вы можете догадаться (и проверить себя). Я бы выбрал вариант 2, потому что с ним не было бы возможности узнать, что драйвер твоей СУБД оказался синхронным, уже отлаживая баги в проде.

Да. Помимо приколов, что в асинхронных методах возвращается Task.FromResult() я ещё сталкивался с обратной ситуацией, когда асинхронный код является основным, а в синхронных методах встречается вызов асинхронного кода с .GetResult(). А ещё веселее, когда подобные преобразования случаются несколько раз при вызове одной функции.


С одной стороны, это хорошо с точки зрения обратной совместимости. Но с другой, теряется смысл в CancellationToken.

А ещё веселее, когда подобные преобразования случаются несколько раз при вызове одной функции.

Вьетнамские флэшбэки от борьбы с Apache NMS...

Oracle конечно виноват, но не виноват… Баг то в коде DbCommand. И только в полном .net framework. В .net 5 это, например, уже исправлено.

То есть в CancellationTokenSource будут храниться все объекты, которые зарегистрировали свой собственный метод отмены/завершения, до окончания работы с этим CancellationTokenSource

Вот это поворот :)

очень интересно, как вы собираете метрики служб?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий