信号量
可以使用信号量作为锁和条件变量。信号量是有一个整数值的对象,可以用两个函数来操作它。在 POSIX 标准中,是sem_wait()和 sem_post()。因为信号量的初始值能够决定其行为,所以首先要初始化信号量,才能调用其他函数与之交互。
sem_wait()要么立刻返回(调用 sem_wait()时,信号量的值大于等于1),要么会让调用线程挂起,直到之后的一个post操作。当然,也可能多个调用线程都调用sem_wait(),因此都在队列中等待被唤醒。
sem_post()并没有等待某些条件满足。它直接增加信号量的值,如果有等待线程,唤醒其中一个。
最后,当信号量的值为负数时,这个值就是等待线程的个数。虽然这个值通常不会暴露给信号量的使用者,但这个恒定的关系值得了解,可能有助于记住信号量的工作原理。
二值信号量(锁)
我们初始化信号量的值为1,就可以把信号量当作锁来使用。
sem_t m;
sem_init(&m, 0, 1); // initialize semaphore to X; what should X be?
sem_wait(&m);
// critical section here
sem_post(&m);
使用信号量作为条件变量
sem_t s;
void *
child(void *arg) {
printf("child\\n");
sem_post(&s); // signal here: child is done
return NULL;
}
int
main(int argc, char *argv[]) {
sem_init(&s, 0, 0); // what should X be?
printf("parent: begin\\n");
pthread_t c;
Pthread_create(c, NULL, child, NULL);
sem_wait(&s); // wait here for child
printf("parent: end\\n");
return 0;
}
把信号量初始化为0时,就可以把它用作条件变量。
有界缓冲区问题
先给出put和get函数的代码:
#define MAX 0xff
int buffer[MAX];
int fill = 0;
int use = 0;
void put(int value)
{
buffer[fill] = value;
fill = fill + 1;
}
int get()
{
int tmp = buffer[use];
use = (use + 1) % MAX;
return tmp;
}
下面是生产者和消费者的代码:
sem_t empty;
sem_t full;
sem_t mutex;
void* producer(void* arg)
{
for (int i = 0; i < loops; i++)
{
sem_wait(&empty);
sem_wait(&mutex);
put(i);
sem_post(&mutex);
sem_post(&full);
}
}
void* consumer(void* arg)
{
int i, tmp = 0;
while (tmp != 1)
{
sem_wait(&full);
sem_wait(&mutex);
tmp = get();
sem_wait(&mutex);
sem_post(&empty);
prirntf("%d\\n", tmp);
}
}
int main()
{
sem_init(&empty, 0, MAX);
sem_init(&full, 0, 0);
sem_init(&mutex, 0, 1);
}
读者-写者锁
typedef struct _rwlock_t {
sem_t lock; // binary semaphore (basic lock)
sem_t writelock; // used to allow ONE writer or MANY readers
int readers; // count of readers reading in critical section
} rwlock_t;
void rwlock_init(rwlock_t *rw) {
rw->readers = 0;
sem_init(&rw->lock, 0, 1);
sem_init(&rw->writelock, 0, 1);
}
void rwlock_acquire_readlock(rwlock_t *rw) {
sem_wait(&rw->lock);
rw->readers++;
if (rw->readers == 1)
sem_wait(&rw->writelock); // first reader acquires writelock
sem_post(&rw->lock);
}
void rwlock_release_readlock(rwlock_t *rw) {
sem_wait(&rw->lock);
rw->readers—;
if (rw->readers == 0)
sem_post(&rw->writelock); // last reader releases writelock
sem_post(&rw->lock);
}
void rwlock_acquire_writelock(rwlock_t *rw) {
sem_wait(&rw->writelock);
}
void rwlock_release_writelock(rwlock_t *rw) {
sem_post(&rw->writelock);
}
哲学家就餐问题
这个问题的基本情况是:假定有 5 位“哲学家”围着一个圆桌。每两位哲学家之间有一把餐叉(一共 5 把)。哲学家有时要思考一会,不需要餐叉;有时又要就餐。而一位哲学家只有同时拿到了左手边和右手边的两把餐叉,才能吃到东西。
评论前必须登录!
注册