import threading
class ZeroEvenOdd:
def __init__(self, n):
self.n = n
self.zero_sem = threading.Semaphore(1)
self.even_sem = threading.Semaphore(0)
self.odd_sem = threading.Semaphore(0)
def zero(self, printNumber):
for i in range(1, self.n + 1):
self.zero_sem.acquire()
printNumber(0)
if i % 2 == 1:
self.odd_sem.release()
else:
self.even_sem.release()
def even(self, printNumber):
for i in range(2, self.n + 1, 2):
self.even_sem.acquire()
printNumber(i)
self.zero_sem.release()
def odd(self, printNumber):
for i in range(1, self.n + 1, 2):
self.odd_sem.acquire()
printNumber(i)
self.zero_sem.release()
#include <functional>
#include <semaphore.h>
class ZeroEvenOdd {
private:
int n;
sem_t zero_sem, even_sem, odd_sem;
public:
ZeroEvenOdd(int n) {
this->n = n;
sem_init(&zero_sem, 0, 1);
sem_init(&even_sem, 0, 0);
sem_init(&odd_sem, 0, 0);
}
void zero(function<void(int)> printNumber) {
for (int i = 1; i <= n; ++i) {
sem_wait(&zero_sem);
printNumber(0);
if (i % 2 == 1)
sem_post(&odd_sem);
else
sem_post(&even_sem);
}
}
void even(function<void(int)> printNumber) {
for (int i = 2; i <= n; i += 2) {
sem_wait(&even_sem);
printNumber(i);
sem_post(&zero_sem);
}
}
void odd(function<void(int)> printNumber) {
for (int i = 1; i <= n; i += 2) {
sem_wait(&odd_sem);
printNumber(i);
sem_post(&zero_sem);
}
}
};
import java.util.concurrent.Semaphore;
class ZeroEvenOdd {
private int n;
private Semaphore zeroSem = new Semaphore(1);
private Semaphore evenSem = new Semaphore(0);
private Semaphore oddSem = new Semaphore(0);
public ZeroEvenOdd(int n) {
this.n = n;
}
public void zero(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
zeroSem.acquire();
printNumber.accept(0);
if (i % 2 == 1) {
oddSem.release();
} else {
evenSem.release();
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for (int i = 2; i <= n; i += 2) {
evenSem.acquire();
printNumber.accept(i);
zeroSem.release();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i += 2) {
oddSem.acquire();
printNumber.accept(i);
zeroSem.release();
}
}
}
class Semaphore {
constructor(count) {
this.count = count;
this.waiters = [];
}
async acquire() {
if (this.count > 0) {
this.count--;
return;
}
await new Promise(resolve => this.waiters.push(resolve));
}
release() {
if (this.waiters.length > 0) {
this.waiters.shift()();
} else {
this.count++;
}
}
}
var ZeroEvenOdd = function(n) {
this.n = n;
this.zeroSem = new Semaphore(1);
this.evenSem = new Semaphore(0);
this.oddSem = new Semaphore(0);
};
ZeroEvenOdd.prototype.zero = async function(printNumber) {
for (let i = 1; i <= this.n; i++) {
await this.zeroSem.acquire();
printNumber(0);
if (i % 2 === 1) {
this.oddSem.release();
} else {
this.evenSem.release();
}
}
};
ZeroEvenOdd.prototype.even = async function(printNumber) {
for (let i = 2; i <= this.n; i += 2) {
await this.evenSem.acquire();
printNumber(i);
this.zeroSem.release();
}
};
ZeroEvenOdd.prototype.odd = async function(printNumber) {
for (let i = 1; i <= this.n; i += 2) {
await this.oddSem.acquire();
printNumber(i);
this.zeroSem.release();
}
};
The "Print Zero Even Odd" problem asks you to implement a class that prints numbers in a specific order using three separate threads. Given a positive integer n
, you must print numbers from 1 to n
in the following sequence:
0 1 0 2 0 3 0 4 ...
That is, a zero is always printed before each number, and the numbers alternate between odd and even. Three different functions are provided, each to be run in its own thread:
zero(printNumber)
: prints zero n
times.even(printNumber)
: prints even numbers (2, 4, ..., n).odd(printNumber)
: prints odd numbers (1, 3, ..., n).At first glance, the problem seems to be about printing numbers in order, but the twist is that three different threads are responsible for printing different parts of the sequence. The challenge is to synchronize these threads so that:
To solve this problem, we use semaphores (or similar synchronization mechanisms) to control which thread can print at any given time. Here is the step-by-step strategy:
Let's walk through the process with n = 3
:
Initial state:
0 1 0 2 0 3
Brute-force approach:
If you attempted to use busy-waiting or polling, the time complexity would be much higher and the program could waste CPU cycles, making it inefficient and potentially incorrect due to race conditions.
Optimized (Semaphore-based) approach:
The "Print Zero Even Odd" problem is a classic thread synchronization challenge. By using semaphores to control the order in which threads print their numbers, we achieve a clean and efficient solution. The key insight is to use synchronization primitives to enforce alternation, ensuring that the output sequence matches the required pattern. This approach avoids busy-waiting and race conditions, and demonstrates a fundamental concept in concurrent programming.