Preliminary. ThinkMeta ConcurrentTasks and this documentation are preliminary; everything is subject to change.

Single-waiter variants

Every primitive that a task can wait on comes in two variants, chosen by SuspensionKind:

Plain name, e.g. Semaphore _s name, e.g. Semaphore_s
Waiting tasks at a time any number (up to 65535) at most one
Cost a lock-free wait list a single slot — cheaper
Timeouts no yes, Wait(Duration)
Waking order LIFO, no fairness —

The pairs: AutoResetEvent / AutoResetEvent_s, ManualResetEvent / ManualResetEvent_s, Semaphore / Semaphore_s, CriticalSection / CriticalSection_s, TaskGroup / TaskGroup_s, WaitableTask / WaitableTask_s, and the waitable I/O requests. The scope guards SemaphoreLock and CriticalSectionLock, and their spinning versions, take either variant.

Choosing

Most waits in a task graph have exactly one waiter: the next stage waits for its input, a coordinator waits for its group, a task waits for its own I/O. Use the _s variant there — it is faster and gives you timeouts. Use the plain variant only where several tasks really wait on the same object at the same time.

Important

An _s primitive with two waiting tasks at the same time is invalid use. Signaling is not limited: any number of tasks and threads may Set or Release an _s primitive.

Destruction

The _s events may be destroyed right after their single waiter has returned from Wait. For the plain variants that is not enough: the signaling side may still be resuming other waiters. Each reference page says exactly when its object may go away.

See also