(11) linux 上的用于线程进程同步的信号量 semaphore,函 sem_init()、sem_destroy()、sem_wait()、sem_post()、

(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)

谢谢

### POSIX 无名信号量 `sem_init` 的用法 #### 数原型 `sem_init` 是用于初始化无名信号量数,其定义如下: ```c int sem_init(sem_t *sem, int pshared, unsigned int value); ``` - 参数说明: - `sem`: 指向信号量对象的指针。 - `pshared`: 如果设置为 0,则表示该信号量仅在同一进程中创建的线程之间共享;如果大于 0,则表示可以在多个进程之间共享。 - `value`: 设置信号量的初始值。 返回值:成功时返回 0,失败则返回 -1 并设置相应的错误码[^1]。 --- #### 使用示例 下面是一个完整的代码示例展示如何使用 `sem_init` 来实现线程间的同步: ```c #include <pthread.h> #include <semaphore.h> #include <stdio.h> #include <unistd.h> #define THREAD_COUNT 2 // 定义全局信号量变量 sem_t semaphore; void* thread_function(void* arg) { printf("Thread %ld is waiting...\n", (long)arg); // 尝试获取信号量 if (sem_wait(&semaphore) != 0) { // P 操作 perror("sem_wait failed"); pthread_exit(NULL); } printf("Thread %ld entered critical section.\n", (long)arg); sleep(1); // 模拟工作时间 printf("Thread %ld leaving critical section.\n", (long)arg); // 释放信号量 if (sem_post(&semaphore) != 0) { // V 操作 perror("sem_post failed"); } return NULL; } int main() { pthread_t threads[THREAD_COUNT]; // 初始化信号量,初始值设为 1 if (sem_init(&semaphore, 0, 1) != 0) { // 初始值为 1 表示最多允许一个线程进入临界区 perror("sem_init failed"); return -1; } // 创建线程 for (long i = 0; i < THREAD_COUNT; ++i) { if (pthread_create(&threads[i], NULL, thread_function, (void*)i) != 0) { perror("pthread_create failed"); return -1; } } // 等待所有线程完成 for (size_t i = 0; i < THREAD_COUNT; ++i) { pthread_join(threads[i], NULL); } // 销毁信号量 if (sem_destroy(&semaphore) != 0) { perror("sem_destroy failed"); } return 0; } ``` 上述代码展示了两个线程通过信号量来控制访问临界区的过程。每次只有一个线程可以进入临界区执行任务[^3]。 --- #### 常见错误及其解决方案 1. **未正确初始化信号量** - 如果调用 `sem_init` 返回 -1,则可能是因为参数不合法或者内存分配失败。 - 需要检查 `errno` 获取具体原因。例如,`EINVAL` 可能意味着非法参数组合,而 `ENOSPC` 或其他资源不足错误表明系统无法支持更多信号量实例。 2. **忘记销毁信号量** - 当不再需要信号量时,应显式调用 `sem_destroy` 进行清理。否则可能导致资源泄漏或后续重新初始化失败。 3. **跨进程共享问题** - 若希望信号量能在不同进程间共享 (`pshared > 0`),需注意目标平台的支持情况以及是否满足特定条件(如地址空间一致性)。通常建议在这种场景下改用命名信号量 `sem_open` 和 `sem_close`[^4]。 --- #### 注意事项 - 对于多线程环境下的无名信号量,推荐始终将 `pshared` 设定为 0,除非确实存在跨进程需求。 - 调试过程中可利用工具链命令验证线程状态,比如运行 `ps aux -L | grep sem` 查看是否有异常挂起现象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值