信号量(Semaphore)是一种用于在多进程或多线程环境中控制对共享资源访问的同步机制。它是由荷兰计算机科学家 Edsger Dijkstra 在 1965 年提出的概念。
信号量可以被看作是一个计数器,用于控制对共享资源的访问。它主要有两种操作:P(等待)和V(释放)。
-
P(等待)操作:当一个进程(或线程)要访问共享资源时,它必须先执行P操作。如果信号量的值大于零,进程可以继续执行临界区代码(访问共享资源),并且信号量的值会减少。如果信号量的值等于零,那么进程将等待,直到信号量的值大于零。
-
V(释放)操作:当一个进程(或线程)完成对共享资源的访问时,它执行V操作,增加信号量的值。这通常会唤醒正在等待的其他进程,使它们可以继续执行。
信号量的一个常见应用是解决生产者-消费者问题,其中生产者和消费者共享一个有限容量的缓冲区。通过使用信号量,可以确保在任何给定时刻只有一个生产者或消费者能够访问缓冲区,以防止竞争条件和数据不一致性。
sem_init:
- 描述:初始化一个信号量。
- 参数:
sem
:指向要初始化的信号量的指针。pshared
:如果非零,则表示信号量在进程之间共享;如果为零,则表示信号量仅在调用进程内可用。value
:信号量的初始值。- 返回值:成功时返回0,失败时返回-1。
sem_destroy:
- 描述:销毁一个信号量。
- 参数:
sem
:指向要销毁的信号量的指针。- 返回值:成功时返回0,失败时返回-1。
sem_wait:
- 描述:执行等待(P)操作,如果信号量的值大于零,则减少其值;否则,阻塞直到信号量变为可用。
- 参数:
sem
:指向信号量的指针。- 返回值:成功时返回0,失败时返回-1。
sem_trywait:
- 描述:尝试执行等待(P)操作,如果信号量的值大于零,则减少其值;否则,立即返回而不阻塞。
- 参数:
sem
:指向信号量的指针。- 返回值:成功时返回0,失败时返回-1。
sem_timedwait:
- 描述:带有超时的等待(P)操作,类似于
sem_wait
,但是在超时之前会立即返回。- 参数:
sem
:指向信号量的指针。abs_timeout
:指定超时的时间。- 返回值:成功时返回0,超时时返回-1。
sem_post:
- 描述:执行释放(V)操作,增加信号量的值。
- 参数:
sem
:指向信号量的指针。- 返回值:成功时返回0,失败时返回-1。
sem_getvalue:
- 描述:获取信号量的当前值。
- 参数:
sem
:指向信号量的指针。sval
:指向存储当前信号量值的整数的指针。- 返回值:成功时返回0,失败时返回-1。
这些函数通常用于在 POSIX 环境中进行多进程或多线程之间的同步。请查阅相关文档以获取更详细的信息和使用示例。
/*
信号量的类型 sem_t
int sem_init(sem_t *sem, int pshared, unsigned int value);
- 初始化信号量
- 参数:
- sem : 信号量变量的地址
- pshared : 0 用在线程间 ,非0 用在进程间
- value : 信号量中的值
int sem_destroy(sem_t *sem);
- 释放资源
int sem_wait(sem_t *sem);
- 对信号量加锁,调用一次对信号量的值-1,如果值为0,就阻塞
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem);
- 对信号量解锁,调用一次对信号量的值+1
int sem_getvalue(sem_t *sem, int *sval);
sem_t psem;
sem_t csem;
init(psem, 0, 8);
init(csem, 0, 0);
producer() {
sem_wait(&psem);
sem_post(&csem)
}
customer() {
sem_wait(&csem);
sem_post(&psem)
}
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
// 创建一个互斥量
pthread_mutex_t mutex;
// 创建两个信号量
sem_t psem;
sem_t csem;
struct Node{
int num;
struct Node *next;
};
// 头结点
struct Node * head = NULL;
void * producer(void * arg) {
// 不断的创建新的节点,添加到链表中
while(1) {
sem_wait(&psem);
pthread_mutex_lock(&mutex);
struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->next = head;
head = newNode;
newNode->num = rand() % 1000;
printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
pthread_mutex_unlock(&mutex);
sem_post(&csem);
}
return NULL;
}
void * customer(void * arg) {
while(1) {
sem_wait(&csem);
pthread_mutex_lock(&mutex);
// 保存头结点的指针
struct Node * tmp = head;
head = head->next;
printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
free(tmp);
pthread_mutex_unlock(&mutex);
sem_post(&psem);
}
return NULL;
}
int main() {
pthread_mutex_init(&mutex, NULL);
sem_init(&psem, 0, 8);
sem_init(&csem, 0, 0);
// 创建5个生产者线程,和5个消费者线程
pthread_t ptids[5], ctids[5];
for(int i = 0; i < 5; i++) {
pthread_create(&ptids[i], NULL, producer, NULL);
pthread_create(&ctids[i], NULL, customer, NULL);
}
for(int i = 0; i < 5; i++) {
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
while(1) {
sleep(10);
}
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
return 0;
}