semaphore operations
P and V operations
semaphore history
concurrency control
computer science fundamentals

What is the original meaning of P and V operations in a context of a semaphore?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The letters P and V come from Dijkstra's original semaphore notation, but modern programmers usually learn the operations by behavior rather than by language history. That is why the names look mysterious at first. The short answer is that P means the operation that waits or decrements, and V means the operation that signals or increments.

The Historical Meaning

These letters came from Dutch terms used by Edsger Dijkstra. The exact expansion of P is discussed in multiple historical retellings, while V is commonly explained as coming from a Dutch word meaning "increase." In practice, the precise expansion matters less than the operational meaning that survived:

  • 'P tries to acquire access'
  • 'V releases access or signals availability'

That is why most modern textbooks translate them as:

  • 'P equals wait, down, or acquire'
  • 'V equals signal, up, or release'

Even when the historical wording varies, the programming meaning is stable.

What the Operations Do

A semaphore stores a counter plus waiting behavior. The two operations are atomic:

  • 'P decrements the semaphore when a resource is available, otherwise it blocks'
  • 'V increments the semaphore and may wake a waiting thread'

For a binary semaphore, the counter behaves a lot like a lock with only two states. For a counting semaphore, the counter can represent multiple available slots.

Mapping P and V to Modern Code

Modern libraries rarely expose methods named P and V. Instead you will see names such as acquire and release. The semantics are the same.

Here is a small Python example that uses a semaphore with one permit:

python
1import threading
2import time
3
4sem = threading.Semaphore(1)
5
6
7def worker(name):
8    print(name, "waiting")
9    sem.acquire()
10    try:
11        print(name, "entered critical section")
12        time.sleep(1)
13    finally:
14        print(name, "leaving critical section")
15        sem.release()
16
17
18threads = [
19    threading.Thread(target=worker, args=("A",)),
20    threading.Thread(target=worker, args=("B",)),
21]
22
23for thread in threads:
24    thread.start()
25
26for thread in threads:
27    thread.join()

In classical notation, sem.acquire() corresponds to P(sem), and sem.release() corresponds to V(sem).

Why the Operations Must Be Atomic

The whole point of semaphores is to coordinate shared state safely. If two threads could decrement the counter at the same time without atomicity, both might believe they acquired the same resource. That would destroy mutual exclusion.

So the names P and V are not just shorthand for arithmetic. They mean:

  • update the semaphore state atomically
  • block or wake threads as needed
  • preserve a concurrency invariant

That is what makes semaphores more powerful than simply incrementing or decrementing a normal integer.

Counting Semaphore Example

Semaphores are especially useful when more than one thread may proceed at once. This example allows two workers into the protected region:

python
1import threading
2import time
3
4sem = threading.Semaphore(2)
5
6
7def worker(i):
8    with sem:
9        print("running", i)
10        time.sleep(0.5)
11
12
13threads = [threading.Thread(target=worker, args=(i,)) for i in range(5)]
14
15for thread in threads:
16    thread.start()
17
18for thread in threads:
19    thread.join()

The first two workers pass immediately. Later workers block until another thread performs the logical V operation by releasing the semaphore.

Common Pitfalls

  • Treating P and V as arbitrary names instead of understanding that they mean atomic wait and signal operations.
  • Assuming semaphores are only for mutual exclusion when counting semaphores also model limited capacity.
  • Forgetting that P may block, which can introduce deadlocks if resource ordering is inconsistent.
  • Calling V too many times and accidentally increasing the permit count beyond the intended limit.
  • Focusing only on the historical words and missing the operational meaning used in modern APIs.

Summary

  • 'P and V are Dijkstra's original semaphore operations.'
  • Modern APIs usually rename them to acquire and release, or wait and signal.
  • 'P means attempt to enter and block if necessary.'
  • 'V means release access and potentially wake a waiter.'
  • The essential idea is not the letters themselves but the atomic coordination they represent.

Course illustration
Course illustration

All Rights Reserved.