Counting semaphores are equipped with two operations, historically denoted as V (also known as signal()) and P (or wait())(see below). Operation V increments the semaphore S, and operation P decrements it. The semantics of these operations is shown below. Square brackets are used to indicate atomic operations, i.e. operations which appear indivisible from the perspective of other processes.
function V(semaphore S):
Atomically increment S
[S ← S + 1]
function P(semaphore S):
repeat:
Between repetitions of the loop other processes may operate on the semaphore
[if S > 0:
Atomically decrement S - note that S cannot become negative
S ← S - 1
break]
The value of the semaphore S is the number of units of the resource that are currently available. The P operation wastes time or sleeps until a resource protected by the semaphore becomes available, at which time the resource is immediately claimed. The V operation is the inverse: it makes a resource available again after the process has finished using it.
Many operating systems provide efficient semaphore primitives which mean that one waiting processes is awoken when the semaphore is free. This means that processes do not waste time checking the semaphore value and context switching unnecessarily.
Monitor has the following features:
- It is associated with an object on demand.
- It is unbound, which means it can be called directly from any context.
- An instance of the Monitor class cannot be created.
The following information is maintained for each synchronized object:
- A reference to the thread that currently holds the lock.
- A reference to a ready queue, which contains the threads that are ready to obtain the lock.
- A reference to a waiting queue, which contains the threads that are waiting for notification of a change in the state of the locked object.
For Windows, critical sections are lighter-weight than mutexes. Mutexes can be shared between processes, but always result in a system call to the kernel which has some overhead. Critical sections can only be used within one process, but have the advantage that they only switch to kernel mode in the case of contention - Uncontended acquires, which should be the common case, are incredibly fast. In the case of contention, they enter the kernel to wait on some synchronization primitive (like an event or semaphore). I wrote a quick sample app that compares the time between the two of them. On my system for 1,000,000 uncontended acquires and releases, a mutex takes over one second. A critical section takes ~50 ms for 1,000,000 acquires. Here's the test code, I ran this and got similar results if mutex is first or second, so we aren't seeing any other effects. | |||||||||
|
From a theoretical perspective, a critical section is a piece of code that must not be run by multiple processes at once because the code accesses shared resources. A mutex is an algorithm (and sometimes the name of a data structure) that is used to protect critical sections. Semaphores and Monitors are common implementations of a mutex. In practice there are many mutex implementation availiable in windows. They mainly differ as consequence of their implementation by their level of locking, their scopes, their costs, and their performance under different levels of contention. See CLR Inside Out - Using concurrency for scalability for an chart of the costs of different mutex implementations. Availiable synchronization primitives. The In the last years much research is done on non-blocking synchronization. The goal is to implement algorithms in a lock-free or wait-free way. In such algorithms a process helps other processes to finish their work so that the process can finally finish its work. In consequence a process can finish its work even when other processes, that tried to perform some work, hang. Usinig locks, they would not release their locks and prevent other processes from continuing.
| ||||
|
A mutex is an object that a thread can acquire, preventing other threads from acquiring it. It is advisory, not mandatory; a thread can use the resource the mutex represents without acquiring it. A critical section is a length of code that is guaranteed by the operating system to not be interupted. In pseudo-code, it would be like: | |||||||||||||
|
Critical Section and Mutex are not Operating system specific, their concepts of multithreading/multiprocessing. Critical Section Is a piece of code that must only run by it self at any given time (for example, there are 5 threads running simultaneously and a function called "critical_section_function" which updates a array... you don't want all 5 threads updating the array at once. So when the program is running critical_section_function(), none of the other threads must run their critical_section_function. mutex* Mutex is a way of implementing the critical section code (think of it like a token... the thread must have possession of it to run the critical_section_code) | |||
|
In Windows, a critical section is local to your process. A mutex can be shared/accessed across processes. Basically, critical sections are much cheaper. Can't comment on Linux specifically, but on some systems they're just aliases for the same thing. | |||
|
In addition to the other answers, the following details are specific to critical sections on windows:
In linux, I think that they have a "spin lock" that serves a similar purpose to the critical section with a spin count. | |||||||
|
The 'fast' Windows equal of critical selection in Linux would be a futex, which stands for fast user space mutex. The difference between a futex and a mutex is that with a futex, the kernel only becomes involved when arbitration is required, so you save the overhead of talking to the kernel each time the atomic counter is modified. A futex can also be shared amongst processes, using the means you would employ to share a mutex. Unfortunately, futexes can be very tricky to implement (PDF). Beyond that, its pretty much the same across both platforms. You're making atomic, token driven updates to a shared structure in a manner that (hopefully) does not cause starvation. What remains is simply the method of accomplishing that.
| ||||
|
Just to add my 2 cents, critical Sections are defined as a structure and operations on them are performed in user-mode context. ntdll!_RTL_CRITICAL_SECTION Whereas mutex are kernel objects (ExMutantObjectType) created in the Windows object directory. Mutex operations are mostly implemented in kernel-mode. For instance, when creating a Mutex, you end up calling nt!NtCreateMutant in kernel. | |||||||
|