目录
- 一、无名信号量的概念
- 二、无名信号量相关函数
- 三、信号量的使用步骤
- 四、应用场景
- 五、测试代码
一、无名信号量的概念
Linux无名信号量(Semaphore)
在Linux操作系统中,信号量(Semaphore)是一种用于进程间或线程间同步和互斥的工具。无名信号量(也被称为POSIX信号量)是POSIX标准的一部分,与System V信号量相比,它提供了更丰富的功能和更好的可移植性。
无名信号量是一种用于控制多个线程或进程对共享资源的访问的计数器。它通常用于实现两个功能。
①同步:确保进程或线程以特定的顺序访问资源。
②互斥:防止两个或多个进程或线程同时访问共享资源。
信号量的值是一个非负整数,通常被称为“许可数”或“资源数”。当信号量的值为0时,表示没有资源可用;当信号量的值大于0时,表示有相应数量的资源可用。
二、无名信号量相关函数
无名信号量相关函数如下:
①sem_init():初始化一个无名信号量。
②sem_destroy():销毁一个无名信号量。
③sem_wait()(也称为sem_trywait()或sem_timedwait()):尝试减少信号量的值。如果信号量的值大于0,则将其减1并立即返回;如果信号量的值为0,则进程将被阻塞,直到信号量的值变为非零。
④sem_post():增加信号量的值。这通常表示释放了一个资源。
头文件
#include <semaphore.h>
函数的详细内容,可查看手册,如 sem_wait() 函数,介绍如下:
三、信号量的使用步骤
使用信号量的步骤:
①在程序全局区定义信号量;
②使用 sem_init()初始化信号量;
③ 使用 sem_wait()和 sem_post()对信号量进行 P/V 操作;
④ 使用 sem_destroy()销毁信号量。
四、应用场景
无名信号量在多线程编程和进程间通信(IPC)中非常有用。以下是一些可能的应用场景:
①生产者-消费者问题:生产者生成数据项并将其放入缓冲区,而消费者从缓冲区中取出数据项并处理它们。使用信号量可以确保生产者在缓冲区满时不会写入数据,而消费者在缓冲区空时不会尝试读取数据。
②读写锁:多个线程可以同时对共享资源进行读操作,但在写操作期间需要阻止其他线程进行读或写操作。信号量可以用于实现这种读写锁。
五、测试代码
程序创建了一个生产者线程和一个消费者线程,线程间通过一个固定大小的缓冲区进行通信。生产者生产整数并将其放入缓冲区,而消费者从缓冲区中取出整数并打印。通过使用信号量empty和full,可以确保生产者不会在缓冲区满时写入数据,而消费者不会在缓冲区空时尝试读取数据。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int in = 0, out = 0;
sem_t empty, full;
void *producer(void *arg) {
for (int i = 0; i < 20; i++) {
sem_wait(&empty); // 等待缓冲区有空位
buffer[in] = i;
printf("Producer produced %d\n", i);
in = (in + 1) % BUFFER_SIZE;
sem_post(&full); // 表示缓冲区有一个新项
}
return NULL;
}
void *consumer(void *arg) {
for (int i = 0; i < 20; i++) {
sem_wait(&full); // 等待缓冲区有项
int item = buffer[out];
printf("Consumer consumed %d\n", item);
out = (out + 1) % BUFFER_SIZE;
sem_post(&empty); // 表示缓冲区有一个空位
}
return NULL;
}
int main() {
pthread_t p, c;
sem_init(&empty, 0, BUFFER_SIZE); // 初始时缓冲区为空,有BUFFER_SIZE个空位
sem_init(&full, 0, 0); // 初始时缓冲区无项
pthread_create(&p, NULL, producer, NULL);
pthread_create(&c, NULL, consumer, NULL);
pthread_join(p, NULL);
pthread_join(c, NULL);
sem_destroy(&empty);
sem_destroy(&full);
return 0;
}
测试结果:
线程中加入 sleep() 函数
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int in = 0, out = 0;
sem_t empty, full;
void *producer(void *arg) {
for (int i = 0; i < 20; i++) {
sem_wait(&empty); // 等待缓冲区有空位
buffer[in] = i;
printf("Producer produced %d\n", i);
in = (in + 1) % BUFFER_SIZE;
sem_post(&full); // 表示缓冲区有一个新项
sleep(1);
}
return NULL;
}
void *consumer(void *arg) {
for (int i = 0; i < 20; i++) {
sem_wait(&full); // 等待缓冲区有项
int item = buffer[out];
printf("Consumer consumed %d\n", item);
out = (out + 1) % BUFFER_SIZE;
sem_post(&empty); // 表示缓冲区有一个空位
sleep(1);
}
return NULL;
}
int main() {
pthread_t p, c;
sem_init(&empty, 0, BUFFER_SIZE); // 初始时缓冲区为空,有BUFFER_SIZE个空位
sem_init(&full, 0, 0); // 初始时缓冲区无项
pthread_create(&p, NULL, producer, NULL);
pthread_create(&c, NULL, consumer, NULL);
pthread_join(p, NULL);
pthread_join(c, NULL);
sem_destroy(&empty);
sem_destroy(&full);
return 0;
}
测试结果如下:
可以看到,第一种程序,因为没有sleep()让出 cpu,会一直生产数据,直到 empty 为空,也就是等待缓冲区空位为0 ,程序阻塞,让出 cpu ,消费者开始进行处理,直到 full 为空,处理完毕,让出 cpu。