Разобрался с APC и реализовал монитор через них напрямую без "семафорной прослойки".

Сравнил скорости работы двух реализаций мониторов: на ядерных Event-ах и на пользовательских APC. Скорость измерял в количестве операций с очередью - поставить/изъять (её код см. выше) в миллисекунду.

AMD Sempron 3000+
Max enqueue (or dequeue) rate per millisecond
2+2=4 threads:
Event --> 332
APC --> 385

50+50=100 threads:
Event --> 266
APC --> 367

APC-шная реализация хоаровского монитора:
## Код:
```
MODULE Hoare;

   IMPORT WinApi;

   TYPE
      Monitor* = POINTER TO LIMITED RECORD
         cs: WinApi.CRITICAL_SECTION
      END;

      Condition* = POINTER TO LIMITED RECORD
         monitor: Monitor;
         n: INTEGER;
         list: ARRAY 256 OF WinApi.HANDLE
      END;

   PROCEDURE NewMonitor* (): Monitor;
      VAR m: Monitor;
   BEGIN NEW(m);
      WinApi.InitializeCriticalSection(m.cs);
      RETURN m
   END NewMonitor;

   PROCEDURE NewCondition* (m: Monitor): Condition;
      VAR c: Condition;
   BEGIN ASSERT(m # NIL, 20);
      NEW(c); c.monitor := m;
      RETURN c
   END NewCondition;

   PROCEDURE (m: Monitor) Enter*, NEW;
   BEGIN WinApi.EnterCriticalSection(m.cs)
   END Enter;

   PROCEDURE (m: Monitor) Exit*, NEW;
   BEGIN WinApi.LeaveCriticalSection(m.cs)
   END Exit;

   PROCEDURE APCEmpty (param: INTEGER);
   END APCEmpty;

   PROCEDURE (c: Condition) Signal*, NEW;
      VAR i, res: INTEGER;
   BEGIN
      IF c.n > 0 THEN
         FOR i := 0 TO c.n - 1 DO
            res := WinApi.QueueUserAPC(APCEmpty, c.list[i], 0);
            res := WinApi.CloseHandle(c.list[i])
         END;
         c.n := 0
      END
   END Signal;

   PROCEDURE (c: Condition) Wait*, NEW;
      CONST access = {0, 1, 3, 4, 16..19}; opt = {};
      VAR res: INTEGER; p: WinApi.HANDLE;
   BEGIN
      IF c.n < LEN(c.list) THEN
         p := WinApi.GetCurrentProcess();
         res := WinApi.DuplicateHandle(p, WinApi.GetCurrentThread(), p, c.list[c.n], access, 0, opt);
         INC(c.n);
         WinApi.LeaveCriticalSection(c.monitor.cs);
         res := WinApi.SleepEx(WinApi.INFINITE, WinApi.TRUE);
         WinApi.EnterCriticalSection(c.monitor.cs)
      ELSE
         WinApi.LeaveCriticalSection(c.monitor.cs);
         WinApi.Sleep(20);
         WinApi.EnterCriticalSection(c.monitor.cs)
      END
   END Wait;

   PROCEDURE (m: Monitor) FINALIZE-;
   BEGIN WinApi.DeleteCriticalSection(m.cs)
   END FINALIZE;

END Hoare.
```
