信号量sem_t

本文详细介绍了信号量的数据类型、初始化方法、关键函数及其应用,包括sem_init、sem_post、sem_wait、sem_destroy等。重点阐述了如何通过信号量实现线程同步,避免竞态条件,并提供了实例演示其在并发编程中的实际应用。

释义

      信号量的数据类型为结构sem_t,它本质上是一个长整型的数。

函数sem_init()用来初始化一个信号量

它的原型为: extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
头文件为: #include <semaphore.h>
sem为指向信号量结构的一个指针;
pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;
value给出了信号量的初始值。
函数sem_post( sem_t *sem )用来增加信号量的值当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。 
函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。
函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
函数sem_destroy(sem_t *sem)用来释放信号量sem。
sem_init的应用
(1)信号量用sem_init函数创建的,下面是它的说明:
#include<semaphore.h>
int sem_init (sem_t *sem, int pshared, unsigned int value);
这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。pshared参数控制着信号量的类型。如果 pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。现在只对不让进程共享的信号量感兴趣。(这个参数受版本影响), Linux线程目前不支持进程间共享信号量,pshared传递一个非零将会使函数返回ENOSYS错误。
(2)这两个函数控制着信号量的值,它们的定义如下所示:
#include <semaphore.h>
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);
这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作"即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。
sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。
信号量这种“只用一个函数就能原子化地测试和设置”的能力下正是它的价值所在。还有另外一个信号量函数sem_trywait,它是sem_wait的非阻塞搭档。sem_trywait是一个立即返回函数,不会因为任何事情阻塞。根据其返回值得到不同的信息。如果返回值为0,说明信号量在该函数调用之前大于0,但是调用之后会被该函数自动减1,至于调用之后是否为零则不得而知了。如果返回值为EAGAIN说明信号量计数为0。
(3) 获得信号量sem的值,并保存到valp中。下面的定义:
#include<semaphore.h>
int sem_getvalue(sem_t *sem, int *valp);
(4) 最后一个信号量函数是sem_destroy。这个函数的作用是在我们用完信号量对它进行清理。下面的定义:
#include<semaphore.h>
int sem_destroy (sem_t *sem);
这个函数也使用一个信号量指针做参数,归还自己占据的一切资源。在清理信号量的时候如果还有线程在等待它,用户就会收到一个错误。
然而在linux的线程中,其实是没有任何资源关联到信号量对象需要释放的,因此在linux中,销毁信号量对象的作用仅仅是测试是否有线程因为该
信号量在等待。如果函数返回0说明没有,正常注销信号量,如果返回EBUSY,说明还有线程正在等待该信号量的信号。
与其它的函数一样,这些函数在成功时都返回“0”。

信号量的使用如下步骤小结

1.声明信号量sem_t sem1;
2.初始化信号量sem_init(&sem1,0,1);
3.sem_post和sem_wait函数配合使用来达到线程同步
4.释放信号量int sem_destroy (sem_t *sem1);

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> #define MAX 10 // 缓冲区大小 #define MAX_PRODUCTS 5 // 生产的产品数量 // 全局变量 char *buffer; // 缓冲区 int product_count = 0; // 当前产品计数 // 信号量 sem_t empty; // 空槽位信号量 sem_t full; // 满槽位信号量 sem_t mutex; // 互斥信号量 // 生产者函数 void* producer(void* arg) { int products_produced = 0; while (products_produced < MAX_PRODUCTS) { // 步骤1:获取互斥锁(使用mutex信号量sem_wait(&mutex); // 步骤2:等待空槽位(使用empty信号量sem_wait(&empty); // 生产产品 product_count++; printf("生产者生产产品 %d\n", product_count); // 模拟向缓冲区写入产品 if (buffer == NULL) { buffer = (char*)malloc(MAX * sizeof(char)); } snprintf(buffer, MAX, "产品 %d", product_count); printf("生产者将产品放入缓冲区: %s\n", buffer); // 步骤3:释放互斥锁 sem_post(&mutex); // 步骤4:通知有满槽位(使用full信号量sem_post(&full); products_produced++; sleep(1); // 模拟生产时间 } printf("生产者完成工作,共生产了 %d 个产品\n", products_produced); return NULL; } // 消费者函数 void* consumer(void* arg) { int products_consumed = 0; while (products_consumed < MAX_PRODUCTS) { // 步骤5:等待满槽位(使用full信号量sem_wait(&full); // 步骤6:获取互斥锁(使用mutex信号量sem_wait(&mutex); // 从缓冲区消费产品 if (buffer != NULL) { printf("消费者从缓冲区取出产品: %s\n", buffer); // 清空缓冲区 memset(buffer, 0, MAX); printf("消费者消费产品\n"); } products_consumed++; // 步骤7:释放互斥锁 sem_post(&mutex); // 步骤8:通知有空槽位(使用empty信号量sem_post(&empty); sleep(2); // 模拟消费时间 } printf("消费者完成工作,共消费了 %d 个产品\n", products_consumed); return NULL; } int main() { pthread_t id_producer, id_consumer; int ret; // 步骤9:初始化信号量 // empty初始值为MAX(有MAX个空槽位) // full初始值为0(没有满槽位) // mutex初始值为1(互斥信号量sem_init(&empty, 0, MAX); sem_init(&full, 0, 0); sem_init(&mutex, 0, 1); // 创建生产者线程 ret = pthread_create(&id_producer, NULL, producer, NULL); if (ret != 0) { printf("创建生产者线程失败\n"); exit(1); } // 创建消费者线程 ret = pthread_create(&id_consumer, NULL, consumer, NULL); if (ret != 0) { printf("创建消费者线程失败\n"); exit(1); } // 等待线程结束 pthread_join(id_producer, NULL); pthread_join(id_consumer, NULL); // 清理资源 if (buffer != NULL) { free(buffer); } // 步骤10:销毁信号量 sem_destroy(&empty); sem_destroy(&full); sem_destroy(&mutex); printf("程序执行完毕\n"); return 0; } 如果做到死锁
最新发布
11-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值