在操作系统理论课上,其实讲授了信号量的原理和使用方式以及使用信号量的优点。相信看到这篇文章的人已经对信号量底层实现机制有了一定的了解,这里就不再过多赘述。本文主要以两个题目为例来讲授信号量如何在高级语言中使用。如果不想费力去弄懂信号量,又想要写并发程序,可以参考go语言。

goroutine机制https://blog.csdn.net/prestyan/article/details/124366846?spm=1001.2014.3001.5501

与信号量紧密相关的两个操作是P,V操作,一些书中还会以wait(s),signal(s)作为示例的伪代码。但是真正要在程序当中使用信号量,其实与所使用的高级语言有关。能不能使用信号量来实现程序并发运行而不出差错,要看使用的语言中有没有提供该函数。如果没有提供,是没办法使用的。并且,所使用的函数形式还与操作系统相关

一个典型的信号量抽象概念:

typedef struct semaphore
{int value;struct process_control_block *lists;
}semaphore;wait(semaphore *s)
{s->value--;if(s->value<0)block(s->lists);
}signal(semaphore *s)
{s->value++;if(s->value<=0)wakeup(s->lists)
}

如果高级语言没有定义或者所给的函数不符合我的预期,可否使用上面的伪代码自定义信号量以及其相关操作?

很明显是不可以的。

因为用户定义的并非原子操作或者说是原子指令,执行过程中达不到想要的目标。除非使用锁操作lock,或者关中断。

_asm("cli");//关中断
codes;
_asm("sti");//开中断

但是效率太低,而且使用了特权指令,不推荐。

C语言中其实提供了非常高效的记录型信号量的函数,如下面第一个例子。本代码都运行在windows环境下。

编写程序模拟场景:

桌上有一空盘,只允许存放一个水果。爸爸专向盘中放橙子,妈妈专向盘中放苹果,女儿专等吃橙子,儿子专等吃苹果。规定当盘空时一次只能放一个水果供吃者自用,请用P,V操作实现爸爸、妈妈、女儿、儿子四个并发进程的同步。

可以推断有四个线程公用一个缓冲区(plate),两个为写(放水果)进程,两个为读进程(拿水果),并且涉及到了生产者与消费者的同步问题。四个进程两两都不能同时进行临界区操作(访问盘子)。因此,需要三个信号量,一个用于控制写进程访问临界区,另外两个用于通知各自写进程对应的读进程临界区满,即放上了相应的水果。

//信号量句柄
HANDLE put,geta,geto;//线程函数
void pthread_put_orange(void *arg);
void pthread_put_apple(void *arg);
void pthread_get_orange(void *arg);
void pthread_get_apple(void *arg);

C语言中句柄可以为pthread,process或者semaphore等。相当于句柄就是具体的信号量。

根据分析,盘子只有一个,因此put信号量初始值设置为1;其余两个信号量初值为0,表示开始没有资源。下面展示了信号量初始化。

void initSemaphore()
{printf("Plate Size: %d\n",plate_size);printf("Plate initial situation: Empty\n");printf("Main thread wait 10s.\n");put=CreateSemaphore(NULL,1,1,NULL);//初始有一个空盘子geta=CreateSemaphore(NULL,0,1,NULL);//没有apple,需要等待puta完毕geto=CreateSemaphore(NULL,0,1,NULL);//没有orange,需要等待puto完毕plate=EMPTY;
}

首先,两个读进程要抢占put信号量,并往里放水果。放完后需要通知相应写进程缓冲区满,写进程从阻塞变为就绪执行完毕后,释放put信号量。注意,put信号量由写进程释放。下面展示了信号量的P,V操作。

void pthread_put_orange(void *arg)
{while(1){WaitForSingleObject(put,INFINITE);//P(put)plate=ORANGE;printf("%s\n","-->Father put 1 orange in plate.");ReleaseSemaphore(geto,1,NULL);//V(geto)Sleep(1000);}
}

写进程收到通知后执行拿水果操作。如果写进程先拿水果,则会被阻塞(get=0),以此实现了题目要求。

void pthread_get_orange(void *arg)
{while(1){WaitForSingleObject(geto,INFINITE);//P(geto)plate=EMPTY;printf("%s\n","-->Daughter get 1 orange from plate.");ReleaseSemaphore(put,1,NULL);//V(put)}
}

整个代码的结构如下:

father()
{P(plate);put orange;V(o);
}
mother()
{P(plate);put apple;V(a);
}
daughter()
{P(o);get orange;V(put);}
son()
{P(a);get apple;V(put);}

这样就全部实现了题目要求。

题目2:

假设有个南北向的桥,仅能容同方向的人顺序走过,相对方向的两个人则无法通过。现在桥南北端都有过桥人。现把每个过桥人当成一个进程,用P,V操作实现管理。

这个题目与上题类似,不一样的是,如果已经有一个进程抢占了临界区,与之相同类型的进程可以直接进入临界区而无需执行P操作。

代码结构如下:

semaphore b=1,s=1,n=1;
int s2n=0,n2s=0;
ps2n()
{P(s);if(s2n==0){P(b);}s2n++;V(s);corss bridge;P(s);s2n--;if(n2s==0){V(b);}V(s);
}
ps2n()
{P(n);if(n2s==0){P(b);}n2s++;V(n);corss bridge;P(n);n2s--;if(n2s==0){V(b);}V(n);
}

读者可以自行实现该题目。

完整代码地址如下:

信号量的使用

C语言中信号量的使用相关推荐

  1. c语言常用的异常处理,C语言中的异常处理

    一 前言: 异常处理,对于做面向对象开发的开发者来说是再熟悉不过了,例如在C#中有 try { ... } catch( Exception e){...} finally{ ..... } 在C++ ...

  2. C语言中信号函数(signal)的使用

    先来简单谈谈C语言中的信号(signal) 首先,signal是C语言库中的函数,它实际上是软中断,也就是软件发出的终端,本质来说,类似于int n. 对于接收到该软中断信号的进程,就会停下手头的工作 ...

  3. C语言中的回调函数(Callback Function)

    C语言中的回调函数(Callback Function) 1 定义和使用场合 回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中 ...

  4. Java与C语言中的锁

    Java与C语言中的锁 C 嵌入式汇编的语法格式是: asm(code : output operand list : input operand list : clobber list) __asm ...

  5. 关于C语言中线程同步的方式

    C语言中线程同步的方式 线程同步 互斥锁 读写锁 条件变量 信号量 线程同步 在多线程环境中,线程之间由于竞争共享资源(临界资源)容易引起数据不一致的问题.一般采用互斥锁(互斥信号量)解决,保证只有一 ...

  6. c语言中程序文件与程序的转换,MCU-C程序基本编程规范(转)

    为了提高源程序的质量和可维护性,从而最终提高软件产品生产力,特编写此规范.本标准规定了程序设计人员进行程序设计时必须遵循的规范.本规范主要针对单片机编程语言和08编译器而言,包括排版.注释.命名.变量 ...

  7. Go 知识点(19)— Go 语言中的野指针

    野指针是一种指向内存位置是不可知的指针,一般是由于指针变量在声明时没有初始化所导致的.在 Go语言中,布尔类型的零值为 false,数值类型的零值为 0,字符串类型的零值为 "", ...

  8. c语言中字符串数组的地址存放以及%s输出单个字符导致程序崩溃的问题

    代码 总结下c语言中字符串数组的地址存放问题 #include <iostream> using namespace std; #include<bits/stdc++.h>i ...

  9. 单片机c语言中的循环语句,单片机c语言教程:C51循环语句

    循环语句是几乎每个程序都会用到的,它的作用就是用来实现需要反复进行多次的操 作.如一个 12M 的 51 芯片应用电路中要求实现 1 毫秒的延时,那么就要执行 1000 次空语句 才能达到延时的目的( ...

最新文章

  1. 十分钟掌握多项式回归:非线性预测
  2. 树莓派应用实例6:测量土壤湿度(改进WEB发布)
  3. HTML5主要新增标签
  4. Thinking in Java方法签名
  5. Android:Eclipse如何删除ADT
  6. sudo chown r mysql_Linux 文件基本属性: chown修改所属组 和 chmod修改文件属性命令
  7. Java基础-----基类Object源码分析
  8. c语言把数字转换为字母,C语言将字符串转数字
  9. JAVA毕设项目教务排课系统(Vue+Mybatis+Maven+Mysql+sprnig+SpringMVC)
  10. SAXReader的使用
  11. 安川机器人外部急停信号点不开_安川机器人示教器常见故障维修方法
  12. 移动铁通宽带上网设置教程
  13. 学习制作FlappyBird时遇到的问题
  14. php职教云答案,职教云答案查询软件下载,职教云提前看答案,职教云php作业答案...
  15. 用Python求矩阵的广义逆
  16. ios开发者平台生成App 专用密码
  17. 简单说说rebuttal
  18. 写字板可以保存html,下列不是写字板可以保存的格式是()
  19. javaScript中三大家族总结
  20. Python numpy.atleast_3d函数方法的使用

热门文章

  1. Navicat连接数据库MySQL报错2059
  2. 前端html大厂面试题
  3. 北漂程序员,年收入200万却想辞职,幸福感为0却放不下家庭
  4. typora主题更改(以及旧版本下载地址)
  5. ReportViewer动态报表开发完整步骤
  6. Linux常用命令、Vim、Shell、计划任务
  7. 判断输入三边能否构成三角形
  8. go语言Web开发框架:项目开发介绍及实战项目介绍
  9. 单纯形法min例题详解_单纯形法例题讲解
  10. 四针角oled屏连接arduino_使用Arduino构建OLED显示屏与Android手机接口的智能手表