Re[16]: .net core и async lock
От: vdimas Россия  
Дата: 12.04.21 08:59
Оценка: -1
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>Когда я рассматриваю источники информации по некоторой теме я ищу в авторе глубину и осторожность.

НС>Ну и? Если ты намекаешь на себя, то у тебя нет ни глубины ни осторожности. Ты растекаешься мыслью вширь

Учёт максимального числа нефункциональных требований — это не вширь, это вглубь.
Это только через понимание того как всё устроено и работает.
А так же обладание некоторой эрудицией в плане стоимости тех или иных решений (особенно если сам их тщательно проверял в разные годы).


НС>при этом совершенно не стесняешься делать далеко идущие выводы даже при недостатке знаний.


При недостатке знаний позволяю себе делать лишь далеко идущие предположения и именно так их преподношу в момент озвучивания, обычно в духе "не могу знать точно, но готов забиться на ящик коньяка".

А вообще отмазка в духе "сам дурак", тебе намекнули на твою категоричность в рассуждениях (вот же, обсуждение на месте), ты решил незамысловато вернуть.


По-факту там не перечислена и половина подводных камней обсуждаемого и альтернативных решений (я не только тебе отвечал в подветке), т.е. "растекаться мыслью" есть еще куда, правда, оно называется не так, это банальная стадия анализа при поиске решения.
Re[16]: .net core и async lock
От: vdimas Россия  
Дата: 12.04.21 10:20
Оценка:
Здравствуйте, artelk, Вы писали:

A>А чем таски тежеловесны? Слишком много полей? Методы слишком много делают?


"Методы делают много" — хорошее определение. ))

Дотнетный Task — это в одном лице и promise, и future, и task, и task_group с т.з. дизайна аналогичных сущностей в других языках/фреймворках.
А так же одновременно "ответная часть" шедулера, которую условно можно назвать "work item", содержащая в себе настройку параметров исполнения этого "work item" шедуллером.

Соответственно, простейшие операции сопровождаются длинющщими цепочками if-ов по куче признаков, в сравнении с дизайном, где упомянутые сущности разделены.

Еще содержат в себе хелперы сугубо для отладки (для представления flow асинхронных задач в Студии), но эти хелперы живы так же в релизе и тоже потребляют свои процессорные тики.

Еще часто оперируют TLS-хранилищем, перетасовывая стек ExecutionContext, что например, порой требует даже постоянной переустановки культуры текущему потоку перед каждым чихом.
(в нейтиве культура привязана к "физическому потоку", то бишь потоку уровня ОС, а в дотнете — к логическому).



A>Полагаю, ты завернешь колбак в тип, реализующий IValueTaskSource.


Это требуется, только если брать готовый awaitable ValueTask.
Но вообще реализовать свой из пары методов будет, наверно, правильней.
Потому что ValueTask тоже продирается через приличный заслон if-ов на каждый чих, потому что его устройство схематично такое: { obj: object, token: short, value: T}.
Если операция закончилась синхронно, то obj равен null, value содержит результат.
Иначе obj — это или Task, или IValueTaskSource, т.е. значение этого поля проверяется на null, потом еще выясняется тип этого поля.

В общем, использовать ValueTask — экономить на спичках.


A>Какую-то очередь тебе в любом случае нужно будет делать.


Разумеется.


A>SemaphoreSlim работает как FIFO (правда, это свойство не отраженов документации, но лучше бы его воспроизвести).


ХЗ.
В недрах ОС базовая очередь потоков одинакового приоритета к ресурсу всегда FIFO.

Отличия от FIFO чаще являются светошумовыми эффектами разных приоритетов, например, при инверсии приоритетов, когда низкоприоритетному потоку дают больше квантов времени, чем его соседям по приоритету, чтобы он освободил ресурс для высокоприоритетного потока.


A>Это будет либо очередь на связном списке, либо что-то с массивами.


Связанный список требует дополнительной аллокации, но в случае попыток сделать lock-free реализацию такого семафора в любом случае будет связанный список.

Если же начать с блокируемых операций (как оно есть сейчас в SemaphoreSlim), то можно и на массиве, тут пофик.
На массиве тоже разные техники есть — от циклического буфера до связанного списка слотов (т.е. массив служит чем-то вроде страницы памяти, в которой выделяются элементы одинакового размера).


A>Если есть основания считать, что твоя очередь лучше — можешь пул реквест сделать.


Тут тоже ХЗ.
В SemaphoreSlim это очередь прокси-задач, полностью захватывающих текущий контекст.
И хотя в обычном случае это не требуется (например, однородный контекст серверной или фоновой программы), в системе смешанных контекстов это может иметь значение (WinForm, плюс FPW, плюс пул потоков).

В общем, техника реализации собсно очереди — это меньший из вопросов.
Если когда-нить дойдут руки, можно будет попробовать и на lock-free очереди это сделать.
Там необходимо будет выполнить специфический lock-free алгоритм — захватить ресурс или встать в очередь, а потом опять попытаться захватить ресурс и выполнить задачу из очереди, бо пока мы ставили задачу в очередь, ресурс могли освободить и просто некому будет проверить очередь до следующей блокировки доступа к ресурсу.

В SemaphoreSlim эта логика дополнительно усложняется тем, что он может работать и как обычный синхронный семафор, т.е. одно только убирание синхронной функциональности упрощает алгоритмы блокировок.

В общем, навскидку сложно сказать, насколько это будет дешевле обычной сериализации доступа через мьютекс (критическую секцию) к состоянию асинхронного семафора, ведь в случае оперирования более легковесными чем Task объектами эта блокировка будут весьма короткой, т.е. вероятность столкновения на ней будет мала.

Поэтому, там только через тестирования различных по нагруженности сценариев.
Или через моделирование, типа как описано здесь:
https://cyberleninka.ru/article/n/matematicheskie-modeli-dlya-kachestvennoy-otsenki-proizvoditelnosti-semaforov-mnogoprotsessornyh-vychislitelnyh-sistem/viewer


A>Это деталь реализации SemaphoreSlim, спрятанная от клиентов. Есть большой шанс, что примут.


А я настороженно отношусь с рассуждениям в духе "подменять текущую реализацию".

Прикинь радость неких товарищей, когда у них после обновления фреймворка вдруг перестанет работать некий код.
И пусть даже вина будет в их собственном кривом коде (или слишком компромиссном из-за компромиссности дотнета), но раньше-то он работал!!!111 ))


A>Еще вариант придумался: попробовать сделать свой собственный хитрый MyValueTask, не требующий боксинга.


Там ничего хитрого.


A>Очередь будет реализована через связный список массивов (примерно как реализован ConcurrentQueue), в которых по значению будут лежать нужные структуры. MyValueTask будет хранить ссылку на внутренний массив и индекс в нем.


Если очередь сделать на массиве, то индекс элемента массива хранить придётся, ес-но.


A>И от объектов в куче мы не избавились — мы массивы создаем.


Я бы ограничился одним массивом.
В реальной жизни конкурентность к ресурсу обычно невысока — от силы десятки или единицы сотен "заявок".

Если же на одном ресурсе сталкиваются тысячи одновременных "заявок" — тут уже стоит думать над дизайном через мат.аппарат СМО (системы массового обслуживания).
В этих случаях обычно очередь выносится выше по архитектуре и обслуживается явно, через знания о характере задач (приоритетах, их взаимных блокировках и прочее).
Какая-нить простейшая очередь семафора этими знаниями не обладает.


A>Переиспользование по кругу массивов (в которых все Task-и completed) не вариант, т.к. мы не знаем, живет ли еще хоть один MyValueTask, ссылающийся на наш массив. Не знаю, будет ли все это заметно эффективней, чем то, как сейчас SemaphoreSlim сделан.


Вскрытие тестирование покажет. ))
Обычно самописные показываемые мною приблуды давали повышение эффективности до 2-3-х раз от стандартных вещей.
И зачастую на ровном месте вызывали ненависть споры до пены (от одного только факта демонстрации более быстрого решения), мол, ускорение конкретно этой операции мало что даёт в целом.

На что у меня стандартный ответ — ваше "в целом" состоит из простейших операций, и если каждая из них ускоряется примерно на столько, то и "целое" заметно ускоряется.
И оно так и есть на практике.

Такие споры обычно быстро доходят до точки "в любом случае всё упирается в долгое обращение к БД, поэтому, там нет смысла экономить на спичках" и поклонники религии "память не ресурс" удаляются с победным видом. ))

Что однажды вылилось в кровавый спор насчёт того, насколько быстродейственны могут быть специализированные сетевые БД, когда оппонентам просто подкинули реальные цифры задержек ответов от тех же бирж, поддерживающих высокочастотный трейдинг (а все ведущие поддерживают).

Ну и, меня уже утомило много лет объяснять фундаментальную ошибку рассуждения "всё-равно мы ждём БД".
Ждать можно по-разному.

Если у выч. системы большой запач по выч. мощности, то довод действует, ОК.
А если выч. система активно нагружена, то чем меньше тиков проца занимает каждый чих, тем позднее настанет надобность в масштабировании или уже имеющееся масштабирование может быть резко экономней.

Т.е., пока текущая задача ждёт отклик БД, проц вполне может быть занят чем-то еще.
Но это, походу, какое-то недоступное простым смертным прям откровение или я ХЗ насчёт причин ответной упоротости.

Придерживаюсь того мнения, что низкоуровневые примитивы инфраструктуры заранее не знают о характере будущих решаемых с помощью них задач, поэтому такие примитивы должны быть выполнены максимально эффективно. В пределе имеет смысл создавать разные такие примитивы (с разными собственными ограничениями) под разные будущие задачи, бо "всемогуттеры" навроде дотнетного Task-а максимально эффективными не могут быть по-определению.
Отредактировано 12.04.2021 10:47 vdimas . Предыдущая версия . Еще …
Отредактировано 12.04.2021 10:46 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:42 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:42 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:37 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:34 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:27 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:25 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:24 vdimas . Предыдущая версия .
Отредактировано 12.04.2021 10:23 vdimas . Предыдущая версия .
Re[6]: .net core и async lock
От: vdimas Россия  
Дата: 12.04.21 16:13
Оценка:
Здравствуйте, artelk, Вы писали:

V>>SemaphoreSlim имеет унутре очередь ожидающих "потоков" (т.е. задач), но эта очередь организована через прокси-задачи.

V>>Т.е. каждый вызов WaitAsync создаёт и регистрирует еще один объект Task, что считается тяжеловесным для такой операции.
A>Не каждый. Если семафор свободен, возвращается переиспользуемый s_trueTask.

Если семафор свободен, то никакой s_trueTask не требуется.
Собсно, ValueTask изначально планировался как объединение {task, value} для случая, когда асинхронная операция закончилась синхронно, т.е. можно сразу использовать результат. IValueTaskSource появился в результате развития функциональности ValueTask для целей сетевого IO, судя по динамике изменений исходников в те годы.


A>А если занят, то полюбому же придется создавать какой-то новый task-like объект в куче и возвращать его (завернутым в ValueTask и т.п.).


Если занят, то в https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.inotifycompletion.oncompleted подаётся делегат-продолжение, который можно вызвать, когда у семафора освободится ресурс.

Собсно, подаваемый делегат — это уже Task-like объект.


A>Если ты что-то другое имел ввиду, просьба развернуть.


Имел ввиду хранить эти уже поданные делегаты в очереди и к ним признак захвата контекста.
Вернее, тут нужен не сам признак, а уже захваченный контекст (при надобности).

Ну и, чтобы не писать постоянно ConfigureAwait(false), я бы в конструктор такого семафора добавил аргумент дефолтного поведения, чтобы получить наиболее простой основной сценарий, например:
public class AsyncSemaphore {
    public AsyncSemaphore(int initialCount, bool defaultContinueOnCapturedContext) {

    ...
}

class Foo {

    AsyncSemaphore _sema = new AsyncSemaphore(N, false);

    ...

    async void Bar() {
        using(await _sema) {
            // do smth with a resource
            ...
        }
    }
}
Отредактировано 13.04.2021 1:03 vdimas . Предыдущая версия .
Re[17]: .net core и async lock
От: artelk  
Дата: 13.04.21 15:04
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, artelk, Вы писали:


A>>А чем таски тежеловесны? Слишком много полей? Методы слишком много делают?

V>...
V>Еще часто оперируют TLS-хранилищем, перетасовывая стек ExecutionContext, что например, порой требует даже постоянной переустановки культуры текущему потоку перед каждым чихом.
V>(в нейтиве культура привязана к "физическому потоку", то бишь потоку уровня ОС, а в дотнете — к логическому).
ExecutionContext хранит AsyncLocals, не хотелось бы их потерять.


V>В общем, использовать ValueTask — экономить на спичках.

ValueTask оптимизирует случай, когда задача завершена синхронно и он позволяет избежать создания объекта Task в куче. Для этого и создан.


A>>SemaphoreSlim работает как FIFO (правда, это свойство не отраженов документации, но лучше бы его воспроизвести).

V>ХЗ.
V>В недрах ОС базовая очередь потоков одинакового приоритета к ресурсу всегда FIFO.
V>Отличия от FIFO чаще являются светошумовыми эффектами разных приоритетов, например, при инверсии приоритетов, когда низкоприоритетному потоку дают больше квантов времени, чем его соседям по приоритету, чтобы он освободил ресурс для высокоприоритетного потока.
Не понял причем тут очередь потоков.

V>В SemaphoreSlim эта логика дополнительно усложняется тем, что он может работать и как обычный синхронный семафор, т.е. одно только убирание синхронной функциональности упрощает алгоритмы блокировок.

+1

V>В общем, навскидку сложно сказать, насколько это будет дешевле обычной сериализации доступа через мьютекс (критическую секцию) к состоянию асинхронного семафора, ведь в случае оперирования более легковесными чем Task объектами эта блокировка будут весьма короткой, т.е. вероятность столкновения на ней будет мала.

Это ты об операции добавления в очередь пишешь? Не понятно как к этому относится тяжеловесность\легковесность объектов Task или их аналогов.


A>>И от объектов в куче мы не избавились — мы массивы создаем.

V>Я бы ограничился одним массивом.
V>В реальной жизни конкурентность к ресурсу обычно невысока — от силы десятки или единицы сотен "заявок".
Как одним? Давая обясню проблему, которую я имел ввиду. Вот вызвал клиент WaitAsync и получил назад MyValueTask. Далее он может его сохранить где-то у себя и проверить у него свойство IsCompleted через час. И значение должно быть верным. Т.е. элемент массива должен быть прибит гвоздями к своему "владельцу" MyValueTask и не может быть переиспользован даже если последний давно стал Complete.
Re[18]: .net core и async lock
От: vdimas Россия  
Дата: 13.04.21 21:33
Оценка: +1
Здравствуйте, artelk, Вы писали:

V>>Еще часто оперируют TLS-хранилищем, перетасовывая стек ExecutionContext, что например, порой требует даже постоянной переустановки культуры текущему потоку перед каждым чихом.

V>>(в нейтиве культура привязана к "физическому потоку", то бишь потоку уровня ОС, а в дотнете — к логическому).
A>ExecutionContext хранит AsyncLocals, не хотелось бы их потерять.

Это конфигурируется и по-умолчанию используется тяжеловесный вариант, как самый безопасный.
Почему и возникла мысль настраивать вариант по-умолчанию.

Всё-таки, AsyncLocals это даже хуже чем TLS, т.е. совсем уж костыль, который крайне редко используется.


V>>В общем, использовать ValueTask — экономить на спичках.

A>ValueTask оптимизирует случай, когда задача завершена синхронно и он позволяет избежать создания объекта Task в куче. Для этого и создан.

Это была изначальная идея, сейчас он чаще служит для обертки над IValueTaskSource в IO, т.е. для избегания создавания полноценного Task на каждую асинхронную IO-операцию.


V>>Отличия от FIFO чаще являются светошумовыми эффектами разных приоритетов, например, при инверсии приоритетов, когда низкоприоритетному потоку дают больше квантов времени, чем его соседям по приоритету, чтобы он освободил ресурс для высокоприоритетного потока.

A>Не понял причем тут очередь потоков.

А очередь каких сущностей ожидает готовности традиционного семафора?


V>>В общем, навскидку сложно сказать, насколько это будет дешевле обычной сериализации доступа через мьютекс (критическую секцию) к состоянию асинхронного семафора, ведь в случае оперирования более легковесными чем Task объектами эта блокировка будут весьма короткой, т.е. вероятность столкновения на ней будет мала.

A>Это ты об операции добавления в очередь пишешь? Не понятно как к этому относится тяжеловесность\легковесность объектов Task или их аналогов.

Этот абзац отвечал на предложение о lock-free реализации очереди.
Алгоритмы lock-free сами по себе достаточно многословны, тут можно делать выводы только через сравнительное тестирование вариантов реализации.

Например, критическая секция (и монитор на основе этой функциональности в дотнете) не дёргает примитивы синхронизации в отсутствии столкновений потоков.
В общем, полно сценариев, когда короткие блокировки заруливают lock-free алгоритмы. Степень заруливаемости зависит от отношения длительности блокировки к среднему периоду м/у обращениями к общему ресурсу.


V>>Я бы ограничился одним массивом.

V>>В реальной жизни конкурентность к ресурсу обычно невысока — от силы десятки или единицы сотен "заявок".
A>Как одним? Давая обясню проблему, которую я имел ввиду. Вот вызвал клиент WaitAsync и получил назад MyValueTask. Далее он может его сохранить где-то у себя и проверить у него свойство IsCompleted через час.

IsCompleted не является обязательным для реализации своих awaiter-ов.

Собсно, на всех углах одно время говорилось (когда многопоточное программирование только набирало популярность) — избегайте опрашивать состояние примитивов синхронизации, это плохая практика.
Ну и вот, можно прямо на уровне дизайна возможность опроса не давать.

Для примера, критическая секция не даёт возможность опросить своё состояние.

Соответственно, семафору или возвращаемой прокси-структуре (при вызове ConfigureAwait(bool) или вызове блокировки с таймаутом) можно будет делать только await.


A>И значение должно быть верным. Т.е. элемент массива должен быть прибит гвоздями к своему "владельцу" MyValueTask и не может быть переиспользован даже если последний давно стал Complete.


Если настолько заморачиваться, то массивы стоит уже выбирать из глобального пула массивов с соответствующими столкновениями на этом синхронизируемом пуле.

В общем, если нужны навороты на все случаи жизни, то они уже есть, толка от своих будет немного.
Что-то выиграть здесь можно лишь на золотом правиле инженерии — самое общее решение является самым неподходящим для конкретной ситуации.

Т.е. выиграть в эффективности в некоем сценарии обычно можно за счёт усиления ограничений, а не за счёт их ослабления.
Отредактировано 13.04.2021 23:30 vdimas . Предыдущая версия . Еще …
Отредактировано 13.04.2021 23:29 vdimas . Предыдущая версия .
Отредактировано 13.04.2021 23:27 vdimas . Предыдущая версия .
Отредактировано 13.04.2021 21:39 vdimas . Предыдущая версия .
Отредактировано 13.04.2021 21:37 vdimas . Предыдущая версия .
Отредактировано 13.04.2021 21:36 vdimas . Предыдущая версия .
Re[19]: .net core и async lock
От: artelk  
Дата: 15.04.21 08:36
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, artelk, Вы писали:


V>>>Отличия от FIFO чаще являются светошумовыми эффектами разных приоритетов, например, при инверсии приоритетов, когда низкоприоритетному потоку дают больше квантов времени, чем его соседям по приоритету, чтобы он освободил ресурс для высокоприоритетного потока.

A>>Не понял причем тут очередь потоков.
V>А очередь каких сущностей ожидает готовности традиционного семафора?
Очередь продолжений, которые нужно выполнить при вызовах Release. Мы ведь асинхронную блокировку обсуждаем?

V>>>В общем, навскидку сложно сказать, насколько это будет дешевле обычной сериализации доступа через мьютекс (критическую секцию) к состоянию асинхронного семафора, ведь в случае оперирования более легковесными чем Task объектами эта блокировка будут весьма короткой, т.е. вероятность столкновения на ней будет мала.

A>>Это ты об операции добавления в очередь пишешь? Не понятно как к этому относится тяжеловесность\легковесность объектов Task или их аналогов.
V>Этот абзац отвечал на предложение о lock-free реализации очереди.
Ну вот создал ты Task (или аналог) за пределами CAS-цикла или блокировки, а внутри вставляешь его в очередь. Каким образом тяжеловесность Task влияет на время блокировки?

V>>>Я бы ограничился одним массивом.

V>>>В реальной жизни конкурентность к ресурсу обычно невысока — от силы десятки или единицы сотен "заявок".
A>>Как одним? Давая обясню проблему, которую я имел ввиду. Вот вызвал клиент WaitAsync и получил назад MyValueTask. Далее он может его сохранить где-то у себя и проверить у него свойство IsCompleted через час.
V>IsCompleted не является обязательным для реализации своих awaiter-ов.
V>...
V>Соответственно, семафору или возвращаемой прокси-структуре (при вызове ConfigureAwait(bool) или вызове блокировки с таймаутом) можно будет делать только await.

С IsCompleted await работает примерно так:
if(myAwaiter.IsCompleted)
{
  var r = myAwaiter.GetResult();
  // выполнить продолжение по месту, без косвенности через делегат
}
else
{
  //...
  myAwaiter.OnComplete(action); // в action то же продолжение
}

В случае, когда awaiter completed это позволяет избежать косвенности вызова через делегат и часто избежать инстанцирования самого делегата (в куче). Мы ведь такты оптимизируем?
В любом случае между вызовом WaitAsync и вызовом OnComplete может быть произвольная задержка, так что вопрос как переиспользовать ячейки массива остается открытим.

A>>И значение должно быть верным. Т.е. элемент массива должен быть прибит гвоздями к своему "владельцу" MyValueTask и не может быть переиспользован даже если последний давно стал Complete.

V>Если настолько заморачиваться, то массивы стоит уже выбирать из глобального пула массивов с соответствующими столкновениями на этом синхронизируемом пуле.
Тогда нужен API для явного возврата MyValueTask в пул и клиентский код обязан это делать.
Re[20]: .net core и async lock
От: vdimas Россия  
Дата: 16.04.21 17:45
Оценка:
Здравствуйте, artelk, Вы писали:

V>>>>Отличия от FIFO чаще являются светошумовыми эффектами разных приоритетов, например, при инверсии приоритетов, когда низкоприоритетному потоку дают больше квантов времени, чем его соседям по приоритету, чтобы он освободил ресурс для высокоприоритетного потока.

A>>>Не понял причем тут очередь потоков.
V>>А очередь каких сущностей ожидает готовности традиционного семафора?
A>Очередь продолжений, которые нужно выполнить при вызовах Release. Мы ведь асинхронную блокировку обсуждаем?

Потоки OS у них называют нитями (thread), а вот эти асинхронные — потоками (flow).
По-русски оба просто потоки.


V>>>>В общем, навскидку сложно сказать, насколько это будет дешевле обычной сериализации доступа через мьютекс (критическую секцию) к состоянию асинхронного семафора, ведь в случае оперирования более легковесными чем Task объектами эта блокировка будут весьма короткой, т.е. вероятность столкновения на ней будет мала.

A>>>Это ты об операции добавления в очередь пишешь? Не понятно как к этому относится тяжеловесность\легковесность объектов Task или их аналогов.
V>>Этот абзац отвечал на предложение о lock-free реализации очереди.
A>Ну вот создал ты Task (или аналог) за пределами CAS-цикла или блокировки, а внутри вставляешь его в очередь.

Или не вставляешь, у нас ведь lock-free, т.е. крутишься в некоем цикле с проверками, вдруг на очередном цикле семафор освободится?
Тогда тяжеловесная задача создана в предыдущих циклах зря.


V>>IsCompleted не является обязательным для реализации своих awaiter-ов.

A>С IsCompleted await работает примерно так:
A>
A>if(myAwaiter.IsCompleted)
A>{
A>  var r = myAwaiter.GetResult();
A>  // выполнить продолжение по месту, без косвенности через делегат
A>}
A>else
A>{
A>  //...
A>  myAwaiter.OnComplete(action); // в action то же продолжение
A>}
A>


И какие проблемы?
Если охота иметь дополнительную семантику — попробовать захватить ресурс без постановки продолжения в очередь, то что мешает выразить эту надобность через дополнительную сигнатуру, например:
SyncGuard TryAcquire();
где SyncGuard является, например, ref struct, имеет public void Dispose() и оператор приведения к bool.

В твоей версии awaiter должен быть disposable, а этого хотелось бы избежать, бо disposable должны только гарды, например, возвращаемый через await некий AsyncGuard.


A>В случае, когда awaiter completed это позволяет избежать косвенности вызова через делегат и часто избежать инстанцирования самого делегата (в куче). Мы ведь такты оптимизируем?


И такты, и "синтаксический оверхед". (С)
В твоей версии надо явно вызывать GetAwaiter(), а потом еще наверчивать конструкции сверху, чтобы освободить семафор автоматом через using.

Если разложить разные сценарии по разным сигнатурам, то будет примерно так:
using var acquired = _sema.TryAcquire();
if(acquired) {
  // do smth sync
  return;
}

using(await _sema) {
  // do smth async
}



A>В любом случае между вызовом WaitAsync и вызовом OnComplete может быть произвольная задержка, так что вопрос как переиспользовать ячейки массива остается открытим.


Тогда зачем тебе эта задержка, если она тебя так напрягает? ))
Пусть _sem.GetAwaiter() не захватывает ресурс.
Пусть ресурс захватывается только в момент вызова OnCompleted(action).


V>>Если настолько заморачиваться, то массивы стоит уже выбирать из глобального пула массивов с соответствующими столкновениями на этом синхронизируемом пуле.

A>Тогда нужен API для явного возврата MyValueTask в пул и клиентский код обязан это делать.

Прям-таки "обязан"? ))
Отредактировано 19.04.2021 17:29 vdimas . Предыдущая версия .
Re[3]: .net core и async lock
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.06.21 11:10
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, Kulibin, Вы писали:


K>>https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-5-asyncsemaphore/


V>Г-но мамонта из 2012-го.

V>Сейчас стоит сделать очередь простых пар {callback:delegate, configuredAwait:bool}, без прокси-задач TaskCompletionSource, как по ссылке.
V>См IValueTaskSource.

Да нашел замену TaskCompletionSource на ManualResetValueTaskSource
https://question-it.com/questions/2068009/ozhidanie-odnogo-sobytija-net-s-pomoschju-valuetask

Кстати он есть в https://source.dot.net/#Microsoft.AspNetCore.Shared.Tests/ManualResetValueTaskSource.cs
и солнце б утром не вставало, когда бы не было меня
Отредактировано 16.06.2021 11:12 Serginio1 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.