Re: .net core и async lock
От: IncremenTop  
Дата: 09.02.21 08:36
Оценка: 4 (1) +3 -4
Здравствуйте, Sharov, Вы писали:

S>но сделать так на прямую нельзя. Как быть? Есть что-то стандартное\библиотечное?


Есть, но скорее всего у тебя неправильная архитектура, раз до такого доходит. Советую пересмотреть и обдумать.
Re: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 08.02.21 20:47
Оценка: 121 (2) +1 :)
Здравствуйте, Sharov, Вы писали:

S>но сделать так на прямую нельзя. Как быть? Есть что-то стандартное\библиотечное?

S>Тут что-то такое мелькало, но не найду сейчас...

https://github.com/rsdn/CodeJam/blob/master/CodeJam.Main/Threading/AsyncLock.cs
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[4]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 11:51
Оценка: 11 (2)
Здравствуйте, Serginio1, Вы писали:

S>Ну судя по описанию вызовется делегат в пуле потоков.


Верно.


S>Ничего морозиться не будет


Ты же сам ниже оставил объяснение:

S>Операция ожидания выполняется потоком из пула потоков. Делегат выполняется рабочим потоком, когда состояние объекта становится сигнальным или истекает интервал времени ожидания. Если timeOutInterval параметр имеет значение, отличный от 0 (ноль), а executeOnlyOnce параметр — false , таймер сбрасывается каждый раз, когда событие получает сигнал, или истекает интервал времени ожидания.

S>[/q]

S>По твоему поток морозится на ожидании сигнала или таймаута.


В одном из потоков пула будет сделан блокирующий вызов WinAPI WaitForSingleObject для соотв хендла, а после возврата из этого вызова будет вызван поданный колбэк.


S>И на каждый хендл свой поток?


Да, что малость забавно, ведь WaitForMultipleObjects может ожидать до 64-х хендлов.
Видать, не стали заморачиваться.
Отредактировано 08.04.2021 13:00 vdimas . Предыдущая версия .
Re: .net core и async lock
От: Слава  
Дата: 08.02.21 19:20
Оценка: 4 (1) +1
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте.


S>А как в .net core обстоят дела с асинхронным локом, есть какой-нибудь примитив а-ля AsyncMonitor?


Через SemaphorSlim(1) и Aquire/Release.
Re[15]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 11.04.21 09:28
Оценка: +1 :)
Здравствуйте, vdimas, Вы писали:

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


Ну и? Если ты намекаешь на себя, то у тебя нет ни глубины ни осторожности. Ты растекаешься мыслью вширь, при этом совершенно не стесняешься делать далеко идущие выводы даже при недостатке знаний.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[4]: .net core и async lock
От: vdimas Россия  
Дата: 08.04.21 13:42
Оценка: 18 (1)
Здравствуйте, Serginio1, Вы писали:

UPD
Ой, уже ответил на это сообщение...
Но оставлю подробности, раз написал.

Операция ожидания выполняется потоком из пула потоков. Делегат выполняется рабочим потоком, когда состояние объекта становится сигнальным или истекает интервал времени ожидания.


Берется поток из пула, к нему засылается WorkItem, который, получив управление из потока, морозит его на блокирующем ожидании хендла.
Допустим, у тебя сотня одновременно-исполняемых задач и ты ожидаешь их исполнения.
Если сделать это через RegisterWaitForSingleObject, то сотня потоков из пула будет выделено на ожидание хендлов HEVENT, соответствующих задачам (эти хендлы создаются в ленивой манере именно в случае надобности блокирующего ожидания).


S>По твоему поток морозится на ожидании сигнала или таймаута.


Ну да.
В псевдокоде тело потоковой процедуры примерно такое:
void ThreadProc(LPARAM lParam) {
  SomeInternalStruct * s = (SomeInternalStruct *)lParam;

  DWORD result = WaitForSingleObject(s->hEvent, s->timeout);
  (s->*callback)(s->userData, result != WAIT_OBJECT_0);
  free(s);
}

где
struct SomeInternalStruct {
  HEVENT hEvent;
  WaitOrTimerCallback callback;
  void * userData;
  int timeout;
};



S>И на каждый хендл свой поток?


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

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

Поэтому, сделано грубо, но надёжно и достаточно эффективно для сценария ожидания совсем небольшого кол-ва таких хендлов.
Т.е., для "выкрутиться в единичном случае" оно пойдёт, но где требуется хотя бы теоретическое масштабирование в будущем — лучше не надо.
Отредактировано 08.04.2021 20:14 vdimas . Предыдущая версия . Еще …
Отредактировано 08.04.2021 20:13 vdimas . Предыдущая версия .
Отредактировано 08.04.2021 13:46 vdimas . Предыдущая версия .
Отредактировано 08.04.2021 13:45 vdimas . Предыдущая версия .
Отредактировано 08.04.2021 13:45 vdimas . Предыдущая версия .
Re[2]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 02:53
Оценка: 7 (1)
Здравствуйте, Serginio1, Вы писали:

S>https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types#tasks-and-wait-handles


Никогда так не делай. ))
Унутре вызывается RegisterWaitForSingleObject, а этого вызова надо избегать — он морозит по одному потоку на каждый такой хендл.

И что хреновее всего, здесь участвует межпоточная сигнализация уровня ОС, а этого в общем случае не требуется, т.к. вызывать асинхронное продолжение можно из текущего потока пула, что эффективнее на порядки.
Re: .net core и async lock
От: Kulibin  
Дата: 09.02.21 15:15
Оценка: 4 (1)
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте.


S>А как в .net core обстоят дела с асинхронным локом, есть какой-нибудь примитив а-ля AsyncMonitor?

S>Мне нужно
S>
S>lock(syncObj)
S>{

S>  await DoAsync()
S>}
S>


S>но сделать так на прямую нельзя. Как быть? Есть что-то стандартное\библиотечное?

S>Тут что-то такое мелькало, но не найду сейчас...

Привет это известная проблема у тебя 2-а варианта использовать monitor на прямую, посмотри во что разворачивается lock (синтаксический сахар)
либо вот тебе ссылки на статью как это делается более навороченным способом

https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-6-asynclock/
https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-5-asyncsemaphore/
Re: .net core и async lock
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.02.21 07:18
Оценка: 3 (1)
Здравствуйте, Sharov, Вы писали:
https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types#tasks-and-wait-handles
и солнце б утром не вставало, когда бы не было меня
Re[2]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 03:02
Оценка: -1
Здравствуйте, IncremenTop, Вы писали:

S>>но сделать так на прямую нельзя. Как быть? Есть что-то стандартное\библиотечное?

IT>Есть, но скорее всего у тебя неправильная архитектура, раз до такого доходит. Советую пересмотреть и обдумать.

Чегой-то? ))
Классический мьютекс — это очередь потоков к ресурсу (обрати внимание, что защищённый мьютексом сценарий назвают "сериализованным").

В кооперативной асинхронщине поверх пула потоков происходящее в точности аналогично, просто очередь переносится из недр ОС в код юзверского уровня исполнения.

Более того, почти всегда очередь можно обслуживать из того же потока, в котором ресурс отпускается, что исключит межпоточную сигнализацию, т.е. не приведёт к профанации кооперативной многозадачности юзверского уровня, ради которой весь этот огород async/await нагородили.

(ИМХО, async/await в управляемых средах — идиотизм сам по себе, бо сответствующая потоковая модель могла быть применена средой исполнения автоматически, без разметки ключевыми словами со стороны программера)
Отредактировано 08.04.2021 13:04 vdimas . Предыдущая версия .
Re[9]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 07.04.21 16:02
Оценка: :)
Здравствуйте, vdimas, Вы писали:

V>>>Так и могут потребоваться разные реализации.

НС>>И зачем это выносить в публичный API?
V>Для кирпичиков инфраструктуры так принято.

Нет.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[11]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 08.04.21 06:17
Оценка: :)
Здравствуйте, vdimas, Вы писали:

НС>>>>И зачем это выносить в публичный API?

V>>>Для кирпичиков инфраструктуры так принято.
НС>>Нет.

V>С пробуждением.

V>OCP из SOLID — давно мейстрим в дотнете, начиная выхода linq и разработанной для него технологии методов-расширений.

Попробуй теперь доказать что OCP и SOLID требует выставлять кишки реализации в публичный API. Я уж не говорю о том что SOLID это совершенно бессистемно надерганный набор принципов и эвристик, и единственный его плюс в том что это модно и молодежно.

V>А да, AwaitableNonDisposable по твоей ссылке теперь тоже лишний, может быть непосредственно заменён на ValueTask.


Лишний. Это писалось до появления ValueTask. Да и сейчас остается поддержка старых платформ, в которых его нет.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[12]: .net core и async lock
От: vdimas Россия  
Дата: 08.04.21 08:12
Оценка: +1
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>С пробуждением.

V>>OCP из SOLID — давно мейстрим в дотнете, начиная выхода linq и разработанной для него технологии методов-расширений.
НС>Попробуй теперь доказать что OCP и SOLID требует выставлять кишки реализации в публичный API.

Кошмар. ))
С каких пор непрозрачные типы стали "кишками"?

И как ты умудрился скипнуть упоминание концептов (пусть они даже явно в языке отсутствуют, но присутствуют в реализации компилятора, который распознаёт некоторые концепты, и еще присуствует в единообразном АПИ многих вещей, т.е. "концепт" держится в голове).


НС>Я уж не говорю о том что SOLID это совершенно бессистемно надерганный набор принципов и эвристик, и единственный его плюс в том что это модно и молодежно.


За 15-20 лет у тебя ничего не изменилось, смотрю? ))
Но мир IT немного изменился.

Просто ты не понимаешь как именно (или лишь тщательно показываешь позицию, согласно которой и не желаешь понимать "всякую ерунду").
Мир IT от забавного кратковременного хаоса периода от конца 90-х до середины нулевых поступательно возвращается к классике конца 70 — второй половины 90-х.
Принципы SOLID выработались в это время, во времена "классики".
SRP 1979г
Liskov P 1987
OCP 1988 — Мейер, начало эпохи контрактного программирования (частично вы его используете в своей либе по ссылке)
DIP (некоторые регулярно путают с IoC, судя по обсуждениям оных на этом сайте, что тоже кошмар) и ISP — естественные следствия этапов декомпозиции проблем, артефакты назначения ролей подсистемам (например, как часть системного подхода к проектированию в СССР)

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

Ведь Мартин был профессионалом и признанным спецом с 70-х годов, но даже еще в середине 90-х не помышлял кричать о SOLID.
(а SOLID — это, несомненно, крик... вернее, раздражительный окрик, или удар линейкой по пальцам, бгг)

Профессионалам эти принципы, понятное дело, как собачке стоп-сигнал, особенно последние два, и их споры об АПИ единичного асинхронного мютекса должны вызвать лишь соотв.улыбку, уместную при наблюдении спора по пустякам.

Потому что-то вообще вслух на эту тему произносить имело смысл разве что о некоем едином АПИ для целого семейства близких по назначению объектов (что я и делал, собсно).

Остальное ("попробуй доказать", "кишки" и т.д.) выглядит как виденная уже не раз специфическая реакция даже на малейшую критику.
(чем задолбал уже порядком, извиняйте за прямоту)
И говорит о том, что взаимодействовать с тобой возможно только вертикально — когда ты командуешь или когда тобой командуют, но никогда горизонтально, где команда совместно вырабатывает решения.
Тут или ты будешь выдавливать людей, или тебя выдавят.
(это предупреждение молодым/неопытным, как грится)
Отредактировано 08.04.2021 8:37 vdimas . Предыдущая версия . Еще …
Отредактировано 08.04.2021 8:36 vdimas . Предыдущая версия .
Re[16]: .net core и async lock
От: vdimas Россия  
Дата: 12.04.21 08:59
Оценка: -1
Здравствуйте, Ночной Смотрящий, Вы писали:

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

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

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


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


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

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


По-факту там не перечислена и половина подводных камней обсуждаемого и альтернативных решений (я не только тебе отвечал в подветке), т.е. "растекаться мыслью" есть еще куда, правда, оно называется не так, это банальная стадия анализа при поиске решения.
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 . Предыдущая версия .
.net core и async lock
От: Sharov Россия  
Дата: 08.02.21 18:38
Оценка:
Здравствуйте.

А как в .net core обстоят дела с асинхронным локом, есть какой-нибудь примитив а-ля AsyncMonitor?
Мне нужно
lock(syncObj)
{

  await DoAsync()
}


но сделать так на прямую нельзя. Как быть? Есть что-то стандартное\библиотечное?
Тут что-то такое мелькало, но не найду сейчас...
Кодом людям нужно помогать!
Re[2]: .net core и async lock
От: Passerby  
Дата: 20.02.21 16:40
Оценка:
У меня примерно такая же проблема.
Есть функция сторонней библиотеки
public IDisposable AddMessageHandler<Tmessage>(string messageName, Action<Tmessage> handler)
    {
      return _hubProxy.On(messageName, message =>
      {
        var decoded = DataConverter.Decode<Tmessage>(message);
        handler(decoded);
      });
    }

В ней надо написать свои Action<Tmessage> handler для разных событий, которые определяются string messageName.

При этом разного рода события, таких как получение цен, размещение своих ордеров, в случае если цены удовлетворяют условию , не должны мешать друг-другу, т.е. должны быть асинхронными, но события одни и те же, размещение ордеров, не должны быть одновременными, т.к. после каждого размещения изменяется баланс, значение которого используется при размещении ордеров. Думаю сделать просто: Action<Tmessage> handler для события "open_orders" написать
Action<string> aOrdersOpen = async (message) =>
    {
      await Task.Delay(1);
lock (lockOrdersOpen)  
{
//код
}
}

А для события получения изменений цен
 Action<string, Bittrex> AOrderBook = async (message)=>
{
//есть редко вызываемый, только когда есть совпадение условий await асинхронный метод и без lock, чтобы не мешать одновременному потоку цен 
}

Попробовал, работает. Как происходит распознавание синхронный Action<string> или нет не знаю, одного async недостаточно потому await Task.Delay(1);. И код работает потому что нет await окончания работы Action, т.е. не вызывается EndInvoke. То что нет вызова EndInvoke у Action это стандарт для событий?
Отредактировано 20.02.2021 17:18 Passerby . Предыдущая версия . Еще …
Отредактировано 20.02.2021 17:17 Passerby . Предыдущая версия .
Re[3]: .net core и async lock
От: Sharov Россия  
Дата: 26.02.21 19:01
Оценка:
Здравствуйте, Passerby, Вы писали:

P>Action<string> aOrdersOpen = async (message) =>

P> {
P> await Task.Delay(1);

1)Зачем это все делать асинхронно?
2)Возможно, что блокирующая очередь будет чем-то полезна...
Кодом людям нужно помогать!
Re[4]: .net core и async lock
От: Passerby  
Дата: 15.03.21 19:28
Оценка:
Здравствуйте, Sharov, Вы писали:
S>1)Зачем это все делать асинхронно?
S>2)Возможно, что блокирующая очередь будет чем-то полезна...
1. Если приходит информация с сервера, на сервер посылается запрос на выполнение некоторых действий. Если в это время не обрабатывать новые данные с сервера, то после выполнения сервером запроса, будут приходить устаревшие данные, которые были отложены к принятию из-за не асинхронности.
2. Не понял зачем блокирующая очередь.
Re[5]: .net core и async lock
От: Sharov Россия  
Дата: 17.03.21 17:17
Оценка:
Здравствуйте, Passerby, Вы писали:

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

S>>1)Зачем это все делать асинхронно?
S>>2)Возможно, что блокирующая очередь будет чем-то полезна...
P>1. Если приходит информация с сервера, на сервер посылается запрос на выполнение некоторых действий. Если в это время не обрабатывать новые данные с сервера, то после выполнения сервером запроса, будут приходить устаревшие данные, которые были отложены к принятию из-за не асинхронности.
P>2. Не понял зачем блокирующая очередь.

В целях синхронизации -- данные обрабатываются по мере поступления. Если не ошибаюсь, возможна разная
конфигурация читателей и писателей -- 1:n, n:n и т.д.
Кодом людям нужно помогать!
Re: .net core и async lock
От: VladCore  
Дата: 17.03.21 17:25
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте.


S>А как в .net core обстоят дела с асинхронным локом, есть какой-нибудь примитив а-ля AsyncMonitor?

S>Мне нужно
S>
S>lock(syncObj)
S>{

S>  await DoAsync()
S>}
S>


S>но сделать так на прямую нельзя. Как быть? Есть что-то стандартное\библиотечное?

S>Тут что-то такое мелькало, но не найду сейчас...

для костыля сойдет. Бест — практис это TaskScheduler с 1м или N рабочимими потоками для той логики которая должна тротлится в один или N потоков максимум. В общем случае. Вы таким образом разделяете ответственность.
Re[2]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 02:38
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>https://github.com/rsdn/CodeJam/blob/master/CodeJam.Main/Threading/AsyncLock.cs


А зачем завернули SemaphoreSlim в матрёшку?
Разве методы-расширения не справились бы?

Ну и, если требуется только асинхронная блокировка, то SemaphoreSlim — это из пушки по воробьям, слишком тяжеловесно, особенно в свете новомодных TaskValue.
В этом смысле SemaphoreSlim устарел.
Отредактировано 06.04.2021 2:48 vdimas . Предыдущая версия .
Re[2]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 03:10
Оценка:
Здравствуйте, Kulibin, Вы писали:

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


Г-но мамонта из 2012-го.
Сейчас стоит сделать очередь простых пар {callback:delegate, configuredAwait:bool}, без прокси-задач TaskCompletionSource, как по ссылке.
См IValueTaskSource.
Re[3]: .net core и async lock
От: Sharov Россия  
Дата: 06.04.21 07:12
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ну и, если требуется только асинхронная блокировка, то SemaphoreSlim — это из пушки по воробьям, слишком тяжеловесно, особенно в свете новомодных TaskValue.

V>В этом смысле SemaphoreSlim устарел.

Что тяжелого с тз асинхронности?
Кодом людям нужно помогать!
Re[3]: .net core и async lock
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 06.04.21 08:15
Оценка:
Здравствуйте, vdimas, Вы писали:

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


S>>https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types#tasks-and-wait-handles


V>Никогда так не делай. ))

V>Унутре вызывается RegisterWaitForSingleObject, а этого вызова надо избегать — он морозит по одному потоку на каждый такой хендл.

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


Ну судя по описанию вызовется делегат в пуле потоков. Ничего морозиться не будет
https://docs.microsoft.com/ru-ru/dotnet/api/system.threading.threadpool.registerwaitforsingleobject?view=net-5.0

RegisterWaitForSingleObjectМетод помещает указанный делегат в очередь пула потоков. Рабочий поток будет выполнять делегат при возникновении одного из следующих событий:
Указанный объект находится в сигнальном состоянии.
Интервал времени ожидания истекает.
RegisterWaitForSingleObject Метод проверяет текущее состояние указанного объекта WaitHandle . Если состояние объекта не сигнальо, метод регистрирует операцию ожидания. Операция ожидания выполняется потоком из пула потоков. Делегат выполняется рабочим потоком, когда состояние объекта становится сигнальным или истекает интервал времени ожидания. Если timeOutInterval параметр имеет значение, отличный от 0 (ноль), а executeOnlyOnce параметр — false , таймер сбрасывается каждый раз, когда событие получает сигнал, или истекает интервал времени ожидания.


По твоему поток морозится на ожидании сигнала или таймаута.
И на каждый хендл свой поток?
и солнце б утром не вставало, когда бы не было меня
Отредактировано 06.04.2021 8:45 Serginio1 . Предыдущая версия .
Re[3]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 06.04.21 10:31
Оценка:
Здравствуйте, vdimas, Вы писали:

V>А зачем завернули SemaphoreSlim в матрёшку?

V>Разве методы-расширения не справились бы?

Методы расширения к чему?
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[4]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 11:43
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>А зачем завернули SemaphoreSlim в матрёшку?

V>>Разве методы-расширения не справились бы?
НС>Методы расширения к чему?

К типу SemaphoreSlim.
Re[4]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 11:54
Оценка:
Здравствуйте, Sharov, Вы писали:

V>>Ну и, если требуется только асинхронная блокировка, то SemaphoreSlim — это из пушки по воробьям, слишком тяжеловесно, особенно в свете новомодных TaskValue.

V>>В этом смысле SemaphoreSlim устарел.
S>Что тяжелого с тз асинхронности?

SemaphoreSlim имеет унутре очередь ожидающих "потоков" (т.е. задач), но эта очередь организована через прокси-задачи.
Т.е. каждый вызов WaitAsync создаёт и регистрирует еще один объект Task, что считается тяжеловесным для такой операции.

http://www.rsdn.org/forum/dotnet/7984271.1
http://www.rsdn.org/forum/dotnet/7984267.1
Re[5]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 06.04.21 13:57
Оценка:
Здравствуйте, vdimas, Вы писали:

НС>>Методы расширения к чему?

V>К типу SemaphoreSlim.

Это приведет к тому, что использование окажется завязанным на семафор, и легко подменить ее, к примеру, на распределенный лок уже не получится.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: .net core и async lock
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 06.04.21 13:58
Оценка:
Здравствуйте, Sharov, Вы писали:

Можно пришпандорить аналог асихронной очереди

https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#asyncproducerconsumercollection
public class AsyncProducerConsumerCollection<T>
{
    private readonly Queue<T> m_collection = new Queue<T>();
    private readonly Queue<TaskCompletionSource<T>> m_waiting =
        new Queue<TaskCompletionSource<T>>();

    public void Add(T item)
    {
        TaskCompletionSource<T> tcs = null;
        lock (m_collection)
        {
            if (m_waiting.Count > 0) tcs = m_waiting.Dequeue();
            else m_collection.Enqueue(item);
        }
        if (tcs != null) tcs.TrySetResult(item);
    }

    public Task<T> Take()
    {
        lock (m_collection)
        {
            if (m_collection.Count > 0)
            {
                return Task.FromResult(m_collection.Dequeue());
            }
            else
            {
                var tcs = new TaskCompletionSource<T>();
                m_waiting.Enqueue(tcs);
                return tcs.Task;
            }
        }
    }
}


С этой структурой данных на месте можно написать следующий код.
C#

Копировать
private static AsyncProducerConsumerCollection<int> m_data = …;

Сделаем сигнальным
m_data.Add(1);


…
await m_data.Take();//Типа Monitor.Enter
......

m_data.Add(1);// Monitor.Exit()
и солнце б утром не вставало, когда бы не было меня
Отредактировано 07.04.2021 7:10 Serginio1 . Предыдущая версия .
Re[6]: .net core и async lock
От: vdimas Россия  
Дата: 06.04.21 19:16
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>>>Методы расширения к чему?

V>>К типу SemaphoreSlim.
НС>Это приведет к тому, что использование окажется завязанным на семафор, и легко подменить ее, к примеру, на распределенный лок уже не получится.

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

А распределенные блокировки или исключительно асинхронные — это малость другие сценарии.
Их все можно обыграть идентичными методами-расширениями, от которых требуется лишь вернуть IDisposable гарды для using.
(в последних версиях эти гарды могут быть даже value-типами, если не ошибаюсь)
Отредактировано 06.04.2021 19:17 vdimas . Предыдущая версия .
Re[7]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 07.04.21 07:46
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Так и могут потребоваться разные реализации.


И зачем это выносить в публичный API?
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[8]: .net core и async lock
От: vdimas Россия  
Дата: 07.04.21 13:32
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>Так и могут потребоваться разные реализации.

НС>И зачем это выносить в публичный API?

Для кирпичиков инфраструктуры так принято.
В данном случае будет АПИ методов-расширений над различными объектами.
Re[10]: .net core и async lock
От: vdimas Россия  
Дата: 07.04.21 20:26
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>>>Так и могут потребоваться разные реализации.

НС>>>И зачем это выносить в публичный API?
V>>Для кирпичиков инфраструктуры так принято.
НС>Нет.

С пробуждением.
OCP из SOLID — давно мейстрим в дотнете, начиная выхода linq и разработанной для него технологии методов-расширений.

Ситуация с тех пор малость забавная — хотя явной сущности "концепт" в дотнете еще нет, но АПИ кирпичиков инфраструктуры теперь разрабатывают так, будто он уже есть.
Типа, в голове держат. ))
В голове компилятора в т.ч., т.к. он реагирует на специические сигнатуры методов и методов-расширений (linq, awaitable и т.д.).

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

В 2007-м году точка в этом споре была поставлена, продолжать брыкаться давно бесполезно.

===============
А да, AwaitableNonDisposable по твоей ссылке теперь тоже лишний, может быть непосредственно заменён на ValueTask.

Да и в целом, попахивает каким-то ненастоящим программированием...
На "отгребись" выполнено, такое можно продавить лишь в среде коллег, резко уступающих в квалификации.
Отредактировано 07.04.2021 20:27 vdimas . Предыдущая версия .
Re[13]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 08.04.21 09:33
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Кошмар. ))

V>С каких пор непрозрачные типы стали "кишками"?

Ответить на вопрос можешь? При чем тут OCP, если ты предлагаешь приватное поле сделать публичным?

V>И как ты умудрился скипнуть упоминание концептов


Это не имеет отношения к обсуждаемому вопросу, очередное проявление твоей привычки растекаться мысью по древу. У меня не так много времени чтобы писать на твои винегретные простыни подробные ответы.

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


А твой ответ выглядит как слив и неспособность аргументировать безаппеляционные заявления, да еще и с хамским переходом на личности.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[14]: .net core и async lock
От: vdimas Россия  
Дата: 08.04.21 11:04
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>Кошмар. ))

V>>С каких пор непрозрачные типы стали "кишками"?
НС>Ответить на вопрос можешь? При чем тут OCP, если ты предлагаешь приватное поле сделать публичным?

OCP-принцип вляется комбинацией критериев Low Coupling/High Cohesion и Protected Variantions.

В том дизайне приватное поле только одно, всё вместе выглядит как фасад над объектом, который уже обладает нужной функциональностью.
Причём, объект "инфраструктурный", т.е. не тянет за собой лишние зависимости, т.к. эти зависимости уже есть "изкаробки".
Плюс объект вряд ли будет выставляться в публичном АПИ использующего его кода, т.е., согласно твоей терминологии, оно и так всё вместе — "кишки".

В таких ситуациях в некоторых ООП-языках уместно защищённое наследование и расширение функциональности, но в полном компромиссов дотнете есть так же инструмент методов-расширений, пригодный для реализации принципов OCP. Плюс к этому "обертки" в дотнете не бесплатны, порождают +1 косвенность на каждом уровне, т.е. еще один аргумент в пользу методов-расширений.


V>>И как ты умудрился скипнуть упоминание концептов

НС>Это не имеет отношения к обсуждаемому вопросу, очередное проявление твоей привычки растекаться мысью по древу. У меня не так много времени чтобы писать на твои винегретные простыни подробные ответы.

Я на это уже отвечал — если зашла речь о возможных различных реализациях, то эти реализации могут понадобиться независимо, а не во взаимно-исключительном виде, как ты рассуждал о "подменить реализацию". Ну и, над неким множеством близких по функциональности объектов допустимо рассуждать о "концептах".


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

НС>А твой ответ выглядит как слив и неспособность аргументировать безаппеляционные заявления, да еще и с хамским переходом на личности.

По дизайну был, скорее, вопрос.
Зато насчёт тяжеловесности реализации SemaphoreSlim — тут да, утверждение. Но я его раскрыл, т.е. на безапелляционность утверждение не тянет — несогласные могут аргументировать.
И не вы же SemaphoreSlim разрабатывали, т.е. так близко принимать было не обязательно. ))
Просто опять нарвался на специфическую ответную "аргументацию".
ОК, забей.

На досуге нарисую легковесную реализацию и выложу сюда, т.к. судя по активности коллег оно может быть народу полезно.
Заодно покажет, как тяжеловесные Task-и можно подменять обычными колбэками (делегатами), которые и так уже подаются state-машинкой в awaiter в готовом виде, т.е. доп. накладных расходов можно избежать практически полностью.
Отредактировано 08.04.2021 11:06 vdimas . Предыдущая версия . Еще …
Отредактировано 08.04.2021 11:05 vdimas . Предыдущая версия .
Re[15]: .net core и async lock
От: Ночной Смотрящий Россия  
Дата: 08.04.21 11:32
Оценка:
Здравствуйте, vdimas, Вы писали:

V>В том дизайне приватное поле только одно


Одно не считается?

V>, всё вместе выглядит как фасад над объектом, который уже обладает нужной функциональностью.


Как слой абстракции.

V>По дизайну был, скорее, вопрос.


Как то очень сумбурно и непонятно ты этот вопрос обозначил. Для ясности:
1) Реализация на базе семафора, возможно, не самая оптимальная. Я не настолько погружен в вопрос, чтобы сейчас быть абсолютно уверенным в этом или в обратном
2) Текущий дизайн, возможно, тоже не идеален. И тут довольно странно переключать акцент на мою личность и мое мнение, потому что этот код писал не я.
3) Дизайн в виде extension-методов к SemaphoreSlim точно неудачный, потому что попытка заменить семафор на какую то другую реализацию приведет к полностью разломанной совместимости.

V>На досуге нарисую легковесную реализацию и выложу сюда, т.к. судя по активности коллег оно может быть народу полезно.


Проще и удобнее для всех сделать PR в CJ.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[16]: .net core и async lock
От: vdimas Россия  
Дата: 08.04.21 12:17
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

V>>В том дизайне приватное поле только одно

НС>Одно не считается?

Сужает сценарии до вызова публичных методов того объекта.
Причём, о реализации интерфейса там речи нет, т.е. нет непреодолимого требования инкапсуляции.

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


V>>, всё вместе выглядит как фасад над объектом, который уже обладает нужной функциональностью.

НС>Как слой абстракции.

Где не происходит оперирования абстрактными типами (т.е. семейством их реализаций).

Я хорошо понимаю эти рассуждения, поэтому и начал с contra, т.е. с недостатков лишних слоёв абстракций в донете.

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

При таких раскладах открывать подробности реализации бывает даже полезно — ведь проект открыт для доработок.
Например, та подробность, что асинхронный мьютекс выполнен поверх слим-семафора является своеобразным TODO для любого использующего библиотеку или просто любопытствующего. Заметь, как резко скакнул дотнет (Core) в кач-ве реализаций инфраструктурных типов после открытия исходников сообществу.
Пару вещей и я туда постил и они вошли в релизы (исправление документации над семейством Vector<N> и решение бага с оптимизацией локальных полей в компиляторе).


НС>2) Текущий дизайн, возможно, тоже не идеален. И тут довольно странно переключать акцент на мою личность и мое мнение, потому что этот код писал не я.


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


НС>3) Дизайн в виде extension-методов к SemaphoreSlim точно неудачный, потому что попытка заменить семафор на какую то другую реализацию приведет к полностью разломанной совместимости.


Предлагалось для всех этих структур создать единый публичный АПИ, в т.ч. не было бы проблем, если у одних методов этот АПИ был в виде методов-расширений, а у других через экземплярные методы. В таком случае в использующем коде (которого много) на уровне исходников ничего не изменилось бы, если тип переменной-"мьютекса" был бы изменён для некоего объекта (в одном месте). Защищаемых подобными мьютексами объектов обычно очень даже счётное количество в реальных проектах, ведь речь тут о длинных блокировках.

Предложение заменить единственную его реализацию на "пространственную блокировку" тут выглядит даже пугающей — поэтому и напрашивается семейтсво с унифицированным АПИ.

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


V>>На досуге нарисую легковесную реализацию и выложу сюда, т.к. судя по активности коллег оно может быть народу полезно.

НС>Проще и удобнее для всех сделать PR в CJ.

Это примерно втрое больший объем работы, включая всеобъемлющие тесты и документирование.
Начну, пожалуй, с того, что пообещал. А остальное будет видно... или у меня, или у кого-то из коллег, реально использующих либу, могут дойти руки.
(я эту CJ не использую, но на самописных легковесных awaitable-объектах съел небольшую собаку, поэтому и высказывался по теме)
Отредактировано 08.04.2021 12:21 vdimas . Предыдущая версия .
Re[12]: SOLID
От: Sharov Россия  
Дата: 09.04.21 12:31
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Попробуй теперь доказать что OCP и SOLID требует выставлять кишки реализации в публичный API. Я уж не говорю о том что SOLID это совершенно бессистемно надерганный набор принципов и эвристик, и единственный его плюс в том что это модно и молодежно.


Требуют, чтобы люди старались по максимум использовать все фишки ООП, а не писать в процедурном стиле +
следили за аккуратность кода в соотв. с принципами SOLID, т.е. почаще рефликсировали о своем коде и SOLID.
Кодом людям нужно помогать!
Re[14]: .net core и async lock
От: vdimas Россия  
Дата: 10.04.21 20:08
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>У меня не так много времени чтобы писать на твои винегретные простыни подробные ответы.


Сергей Тепляков
http://sergeyteplyakov.blogspot.com/2019/12/blog-post.html

Главный навык программиста

Я заметил, что опытных специалистов из разных областей объединяет как минимум одна вещь: осторожность в суждениях.

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

Нужно ли писать тесты? Важно ли качество кода? Какая лучшая степень прожарки мяса? С чем лучше всего сочетается белое вино? Нужно ли делать жим лежа со становой или турника будет достаточно?

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

Re[5]: .net core и async lock
От: artelk  
Дата: 11.04.21 12:11
Оценка:
Здравствуйте, vdimas, Вы писали:

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

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

Не каждый. Если семафор свободен, возвращается переиспользуемый s_trueTask.
А если занят, то полюбому же придется создавать какой-то новый task-like объект в куче и возвращать его (завернутым в ValueTask и т.п.).
Можно подумать в сторону пула таких объектов, но на стороне клиентского кода потребовалось бы явно возвращать их в пул, а это менее удобно.
Если ты что-то другое имел ввиду, просьба развернуть.
Re[15]: .net core и async lock
От: artelk  
Дата: 11.04.21 13:19
Оценка:
Здравствуйте, vdimas, Вы писали:

V>На досуге нарисую легковесную реализацию и выложу сюда, т.к. судя по активности коллег оно может быть народу полезно.

Хотелось бы посмотреть.

V>Заодно покажет, как тяжеловесные Task-и можно подменять обычными колбэками (делегатами), которые и так уже подаются state-машинкой в awaiter в готовом виде, т.е. доп. накладных расходов можно избежать практически полностью.

https://github.com/microsoft/referencesource/blob/master/mscorlib/system/threading/Tasks/Task.cs#L146
А чем таски тежеловесны? Слишком много полей? Методы слишком много делают?

Полагаю, ты завернешь колбак в тип, реализующий IValueTaskSource. Далее завернешь этот тип в ValueTask. Конструктор ValueTask принимает интерфейс, т.е. твой тип, даже если это была структура, будет жить в куче.
Какую-то очередь тебе в любом случае нужно будет делать. SemaphoreSlim работает как FIFO (правда, это свойство не отраженов документации, но лучше бы его воспроизвести). Это будет либо очередь на связном списке, либо что-то с массивами. Если есть основания считать, что твоя очередь лучше — можешь пул реквест сделать. Это деталь реализации SemaphoreSlim, спрятанная от клиентов. Есть большой шанс, что примут.

Еще вариант придумался: попробовать сделать свой собственный хитрый MyValueTask, не требующий боксинга. Очередь будет реализована через связный список массивов (примерно как реализован ConcurrentQueue), в которых по значению будут лежать нужные структуры. MyValueTask будет хранить ссылку на внутренний массив и индекс в нем. Свойство IsCompleted, в частности, будет смотреть в элемент этого массива и возвращать значение оттуда. В теории, должно получится Правда, "отработанные" массивы будут забираться GC только если все инстансы MyValueTask, на них ссылающиеся, удалятся. И от объектов в куче мы не избавились — мы массивы создаем. Переиспользование по кругу массивов (в которых все Task-и completed) не вариант, т.к. мы не знаем, живет ли еще хоть один MyValueTask, ссылающийся на наш массив. Не знаю, будет ли все это заметно эффективней, чем то, как сейчас SemaphoreSlim сделан.
Отредактировано 12.04.2021 8:55 artelk . Предыдущая версия . Еще …
Отредактировано 11.04.2021 13:50 artelk . Предыдущая версия .
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[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...
Пока на собственное сообщение не было ответов, его можно удалить.