使用信号量实现没有饥饿问题的锁 (假设信号量为 weak semaphores)

实现没有饥饿问题的锁。

实现算法:
  Morris's solution
  It uses two turnstiles to create two waiting rooms before the critical section.The mechanism works in two phases.
  During the first phase, the first turnstile is open and the second is closed, so threads accumulate in the second room.During the second phase, the first turnstile is closed, so no new threads can enter, and the second is open, so the existing threads can get to the critical section.

成员变量

成员变量的初始化

算法框架

Morris 算法

   mutex 用于保护临界区变量 room1, 在t1.wait()部分实现了等待线程到达room1的过程, if 语句表示只要room1中还有线程, 就让room1的线程进入 room2。 并且所有从room1过来的线程都"卡在"room2中(即都会在 t2.wait()处等待)。
  当不再有新的线程进入 room1时(即当前room1中已经没有线程了), 关闭room1, 打开 room2
  “打开” room2 对应代码中就是 t2.signal()部分, 而"关闭" room1 其实就是不去 signal t1 。

  t2.wait()部分用于处理 room2 中的每个线程, 由于t2初始化为 0,并且最后一个 room1room2 的线程 signal(打开)t2。t2 初始值为0, 因此每次 room2中只有一个线程对临界区进行操作。

数据结构和初始化

typedef struct __ns_mutex_t {
	// room1, room2 计数
    // lock 保护临界区的 room1
    // 对于 room2 后面可以看到, 间接地由 s1 和 s2 保护
	int room1, room2;
	sem_t lock;

	sem_t s1, s2;
} ns_mutex_t;

void ns_mutex_init(ns_mutex_t *m) {
	m->room1 = 0;
	m->room2 = 0;
	sem_init(&m->lock, 0, 1);
	sem_init(&m->s1, 0, 1);
	sem_init(&m->s2, 0, 0);
}

获取/释放 锁

void ns_mutex_acquire(ns_mutex_t *m) {
	sem_wait(&m->lock);
	m->room1 ++;
	sem_post(&m->lock);

	/* ---let threads goto room2--- */
	sem_wait(&m->s1);
	m->room2 ++;
	sem_wait(&m->lock);
	m->room1 --;
	if (m->room1) {
		sem_post(&m->lock);
		sem_post(&m->s1);
	/* --- room1 exits threads--- */
	} else {
		sem_post(&m->lock);
		sem_post(&m->s2);
		/* ---当 room1 中最后一个线程进来后, "关闭"s1, "打开"s2"--- */
	}

	sem_wait(&m->s2);
	m->room2 --;
}

void ns_mutex_release(ns_mutex_t *m) {
	if (m->room2) {
		/* ---处理处理 room2 中的下一个线程--- */
		sem_post(&m->s2);
	} else {
		/* ---room2 线程全部拿到锁并执行完毕, 重新"打开" s1--- */
		sem_post(&m->s1);
	}
}
参考书籍 LittleBookOfSemaphores

你可能感兴趣的:(C/C++,算法)