(1)先给出一言的教导:
(2)信号量属于 linux 内核里的知识。缺乏源代码的支撑,所以比较难以理解。以下给出网上的大师们的讲解的源代码:
++解析:
wait(S)、signal(S) 也可以记为P(S)、V(S),这对原语可用于实现系统资源的“申请”和“释放”。
S.value 的初值表示系统中某种资源的数目。
对信号量 S的一次 P操作意味着进程请求一个单位的该类资源,因此需要执行 S.value–, 表示资源数减 1,当 S.value<0时表示该类资源已分配完毕,因此进程应调用 block原语进行自我阻塞(当前运行的进程从运行态阻塞态),主动放弃处理机,并插入该类资源的等待队列 S.L中。可见,该机制遵循了“让权等待”原则,不会出现“忙等”现象。
对信号量 S的一次 V操作意味着进程释放一个单位的该类资源,因此需要执行 S.value++,表示资源数加 1,若加 1后仍是 S.value<= 0,表示依然有进程在等待该类资源,因此应调 用wakeup原语唤醒等待队列中的第一个进程(被唤醒进程从阻塞态>就绪态)。
前一个进程释放一个资源就唤醒一个新的进程进行使用。
(3)介绍介绍关于信号量的重要函数的定义与使用, sem_init():
++代码举例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define NUM_THREADS 2
sem_t semaphore; // 信号量保护下面的全局变量
int shared_resource = 0;
void* thread_func(void* arg)
{
int thread_num = *(int*)arg; // 计算形参的值
sem_wait(&semaphore); // 等待信号量
printf("Thread %d: Accessing shared resource (value: %d)\n", thread_num, shared_resource);
shared_resource++; // 访问共享资源
sleep(1); // 模拟处理时间
printf("Thread %d: Done with shared resource (new value: %d)\n", thread_num, shared_resource);
sem_post(&semaphore); // 释放信号量
return NULL;
}
int main()
{
pthread_t threads[NUM_THREADS]; // 创建两个子线程
int thread_args[NUM_THREADS];
if (sem_init( &semaphore, 0, 1 ) != 0) { // 初始化信号量,只有 1 个共享资源,用于线程间同步
perror("sem_init"); exit(EXIT_FAILURE);
}
for (int i = 0; i < NUM_THREADS; i++) // 创建线程
{
thread_args[i] = i + 1; // 设置线程函数里的参数
if ( pthread_create( &threads[i], NULL, thread_func, &thread_args[i]) != 0 ) {
perror("pthread_create"); exit( EXIT_FAILURE );
}
}
for (int i = 0; i < NUM_THREADS; i++) // 等待所有线程完成
pthread_join(threads[i], NULL);
sem_destroy(&semaphore); // 销毁信号量
return 0;
}
++ 上面的例子代码中:在这个示例中,我们创建了两个线程,它们通过信号量来同步对共享资源的访问。信号量的初始值为 1,这意味着一次只能有一个线程访问共享资源。sem_wait 函数用于等待信号量,而 sem_post 函数用于释放信号量。
(4)函数 sem_destroy():
(5)函数 sem_wait():
(6)函数 sem_post ():
++ 以下是一个使用 sem_post 和 sem_wait 的简单示例,展示了如何在生产者-消费者模型中同步对缓冲区的访问。创建了一个生产者线程和一个消费者线程,它们通过信号量和互斥锁来同步对共享缓冲区的访问:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE]; // 可以存储 10 个数据
int count = 0; // 缓冲区中当前存储的项数
pthread_mutex_t mutex ; // 保护对缓冲区和计数的访问的互斥锁
sem_t empty ; // 表示缓冲区中空位的信号量
sem_t full ; // 表示缓冲区中已占用位的信号量
void* producer(void* arg)
{
for (int i = 0; i < 20; i++) // 生产20个项
{
int item = rand() % 100 ; // 生成一个随机项
sem_wait( &empty ); // 等待缓冲区中有空位。先拿到空信号量
pthread_mutex_lock(&mutex); // 锁定缓冲区
buffer[count] = item; // 将项放入缓冲区
count++;
printf("Produced: %d\n", item);
pthread_mutex_unlock(&mutex); // 解锁缓冲区
sem_post(&full); // 增加表示已占用位的信号量,发布一次满信号量
sleep(rand() % 2); // 模拟生产时间
}
return NULL;
}
void* consumer(void* arg)
{
for (int i = 0; i < 20; i++) // 消费20个项
{
sem_wait( &full ); // 等待缓冲区中有已占用位,先拿到满信号量,表示有数据可消费
pthread_mutex_lock(&mutex) ; // 锁定缓冲区
int item = buffer[count - 1] ; // 从缓冲区中取出项
count--;
printf("Consumed: %d\n", item) ;
pthread_mutex_unlock(&mutex) ; // 解锁缓冲区
sem_post( &empty ); // 增加表示空位的信号量,发布一次空信号量
sleep( rand() % 2 ); // 模拟消费时间
}
return NULL;
}
int main()
{
pthread_t prod_thread, cons_thread ; // 要创建两个线程
sem_init( &empty, 0, BUFFER_SIZE ); // 标识初始的时的资源数量为 10
sem_init( &full , 0, 0 ); // 标识初始的时的资源数量为 0
// 所以即使消费线程先运行也要陷入睡眠
pthread_mutex_init( &mutex, NULL) ; // 初始化互斥锁
pthread_create( &prod_thread, NULL, producer, NULL) ; // 创建生产者和消费者线程
pthread_create( &cons_thread, NULL, consumer, NULL) ;
pthread_join( prod_thread, NULL ) ; // 等待线程完成
pthread_join( cons_thread, NULL ) ;
sem_destroy( &empty ) ; // 销毁信号量和互斥锁
sem_destroy( &full ) ;
pthread_mutex_destroy( &mutex );
return 0;
}
++ 给出上面例子的解释:
(7)
谢谢