Информация об изменениях

Сообщение Re[4]: .net core и async lock от 08.04.2021 13:42

Изменено 08.04.2021 13:45 vdimas

Re[4]: .net core и async lock
Здравствуйте, Serginio1, Вы писали:

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


А что тут непонятно?

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


Берется поток из пула, к нему засылается 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;
  int timeout;
};



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


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

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

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

Поэтому, сделано грубо, но надёжно и достаточно эффективно для сценария ожидания совсем небольшого кол-ва таких хендлов.
Т.е., для "выкрутиться в единичном случае" оно пойдёт, но где требуется хотя бы теоретическое масштабирование в будущем — лучше не надо.
Re[4]: .net core и async lock
Здравствуйте, 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;
  int timeout;
};



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


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

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

Поэтому, сделано грубо, но надёжно и достаточно эффективно для сценария ожидания совсем небольшого кол-ва таких хендлов.
Т.е., для "выкрутиться в единичном случае" оно пойдёт, но где требуется хотя бы теоретическое масштабирование в будущем — лучше не надо.