四十三、Linux 线程——线程同步之线程信号量
43.1 信号量
43.1.1 信号量介绍
- 信号量从本质上是一个非负整数计数器,是共享资源的数目,通常被用来控制对共享资源的访问
- 信号量可以实现线程的同步和互斥
- 通过 sem_post() 和 sem_wait() 函数对信号量进行加减操作从而解决线程的同步和互斥
- 信号量数据类型:sem_t
43.1.2 信号量创建和销毁
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned value);
int sem_destroy(sem_t *sem);
- 函数参数:
- sem:信号量指针
- pshared:是否在进程间共享的标识,0 为不共享,1 为共享
- value:信号量的初始值
- 返回值:成功,返回 0;出错,返回错误编号
43.1.3 信号量的加和减操作
#include <semaphore.h>
/** 增加信号量的值 */
int sem_post(sem_t *sem); /** 非阻塞版本,减少信号量的值 */
int sem_wait(sem_t *sem); /** 阻塞版本,减少信号量的值 */
int sem_trywait(sem_t *sem);
- 函数返回值:成功返回0;出错返回错误编号
- 函数说明:
- 调用 sem_post() 一次信号量作 +1 操作
- 调用 sem_wait() 一次信号量作 -1 操作
- 当线程调用 sem_wait() 后,若信号量的值小于 0 ,则线程阻塞。只有其他线程在调用 sem_post() 对信号量作加操作后,并且其值大于或等于 0 时,阻塞的线程才能继续运行
43.2 例子
43.2.1 例子1
#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h> /** 定义线程信号梁 */
sem_t sem1;
sem_t sem2; void *a_fn(void *arg)
{
sem_wait(&sem1);
printf("thread a running\n");
return (void *);
} void *b_fn(void *arg)
{
sem_wait(&sem2);
sem_post(&sem1); ///< 释放线程 a
printf("thread b running\n");
return (void *);
} void *c_fn(void *arg)
{
sem_post(&sem2); ///<释放线程 b,对线程信号量 sem2 做 +1 操作,让阻塞的线程 b 运行
printf("thread c running\n");
return (void *);
} int main(void)
{
pthread_t a, b ,c; /** 线程信号量初始化 */
sem_init(&sem1, , );
sem_init(&sem2, , ); pthread_create(&a, NULL, a_fn, (void *));
pthread_create(&b, NULL, b_fn, (void *));
pthread_create(&c, NULL, c_fn, (void *)); pthread_join(a, NULL);
pthread_join(b, NULL);
pthread_join(c, NULL); sem_destroy(&sem1);
sem_destroy(&sem2); return ;
}
编译运行结果:

43.2.2 PV操作银行账户
- P 操作:减,如减 1 操作 sem_wait()
- V 操作:加,加 1 操作 sem_post()

atm_count.h
#ifndef __ATM_ACCOUNT_H__
#define __ATM_ACCOUNT_H__ #include <math.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h> /** 账户信息 */
typedef struct {
int code; ///< 银行账户的编码
double balance; ///< 账户余额 /** 建议互斥锁用来锁定一个账户(共享资源),和账户绑定再一起,
* 尽量不设置成全局变量,否则可能出现一把锁去锁几百个账户,导致并发性能降低 */
//pthread_mutex_t mutex; ///< 定义一把互斥锁,用来对多线程操作的银行账户(共享资源)进行加锁 //pthread_rwlock_t rwlock; ///<定义读写锁 //定义线程信号量
25 sem_t sem;
}atm_Account; /** 创建账户 */
extern atm_Account *atm_account_Create(int code, double balance);
/** 销毁账户 */
extern void atm_account_Destroy(atm_Account *account);
/** 取款 */
extern double atm_account_Withdraw(atm_Account *account, double amt);
/** 存款 */
extern double atm_account_Desposit(atm_Account *account, double amt);
/** 查看账户余额 */
extern double atm_account_BalanceGet(atm_Account *account); #endif
atm_account.c
#include "atm_account.h" /** 创建账户 */
atm_Account *atm_account_Create(int code, double balance)
{
atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account));
if(NULL == account) {
return NULL;
} account->code = code;
account->balance = balance; /** 对互斥锁进行初始化 */
//pthread_mutex_init(&account->mutex, NULL); /** 初始化读写锁 */
//pthread_rwlock_init(&account->rwlock, NULL); /** 初始化线程信号量 */
21 sem_init(&account->sem, 0, 1);
return account;
} /** 销毁账户 */
void atm_account_Destroy(atm_Account *account)
{
if(NULL == account){
return ;
} //pthread_mutex_destroy(&account->mutex);
//pthread_rwlock_destroy(&account->rwlock); ///< 销毁读写锁 /** 销毁线程信号量 */
37 sem_destroy(&account->sem);
free(account);
} /** 取款: 成功,则返回取款金额 */
double atm_account_Withdraw(atm_Account *account, double amt)
{
if(NULL == account) {
return 0.0;
} /** 对共享资源(账户进行加锁) */
//pthread_mutex_lock(&account->mutex);
//pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁
/** P(1) 操作 */
52 sem_wait(&account->sem);
if(amt < || amt > account->balance) {
//pthread_mutex_unlock(&account->mutex);
//pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 /** V(1) 操作 */
59 sem_post(&account->sem);
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp -= amt;
account->balance = balance_tmp; //pthread_mutex_unlock(&account->mutex);
//pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 /** V(1) 操作 */
72 sem_post(&account->sem);
return amt;
} /** 存款: 返回存款的金额 */
double atm_account_Desposit(atm_Account *account, double amt)
{
if(NULL == account){
return 0.0;
} /** 对共享资源(账户进行加锁) */
//pthread_mutex_lock(&account->mutex);
//pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁 /** P(1) 操作 */
88 sem_wait(&account->sem);
if(amt < ){
//pthread_mutex_unlock(&account->mutex);
//pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 /** V(1) 操作 */
95 sem_post(&account->sem);
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp += amt;
account->balance = balance_tmp; //pthread_mutex_unlock(&account->mutex);
//pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 /** V(1) 操作 */
108 sem_post(&account->sem);
return amt;
} /** 查看账户余额 */
double atm_account_BalanceGet(atm_Account *account)
{
if(NULL == account){
return 0.0;
} /** 对共享资源(账户进行加锁) */
//pthread_mutex_lock(&account->mutex);
//pthread_rwlock_rdlock(&account->rwlock); ///< 上读锁 /** P(1) 操作 */
124 sem_wait(&account->sem);
double balance_tmp = account->balance;
//pthread_mutex_unlock(&account->mutex);
//pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 /** V(1) 操作 */
131 sem_post(&account->sem);
return balance_tmp;
}
编译运行结果:

43.2.3 利用线程信号量实现线程之间的同步

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h> /** 两个线程定义的共享资源 */
typedef struct {
int res;
sem_t sem;
}Result; /** 计算并将结果放置在 Result 中的线程运行函数 */
void *set_fn(void *arg)
{
Result *r = (Result *)arg;
int i = ;
int sum = ; for(; i <= ; i++){
sum += i;
} /** 将结果放置到 Result 中 */
r->res = sum; sem_post(&r->sem);
return (void *);
} /** 获得结果的线程运行函数 */
void *get_fn(void *arg)
{
Result *r = (Result *)arg; sem_wait(&r->sem);
/** 去获取计算结果 */
int res = r->res;
printf("0x%lx get sum is %d\n", pthread_self(), res); return (void *);
} int main(void)
{
int err;
pthread_t cal, get; Result r;
sem_init(&r.sem, , );
/** 启动获取结果的线程 */
if((err = pthread_create(&get, NULL, get_fn, (void *)&r)) != ){
perror("pthread create error");
} /** 启动计算结果的线程 */
if((err = pthread_create(&cal, NULL, set_fn, (void *)&r)) != ){
perror("pthread create error");
} pthread_join(cal, NULL);
pthread_join(get, NULL);
sem_destroy(&r.sem);
return ;
}
编译运行:

四十三、Linux 线程——线程同步之线程信号量的更多相关文章
- Linux并发与同步专题 (3) 信号量
关键词:Semaphore.down()/up(). <Linux并发与同步专题 (1)原子操作和内存屏障> <Linux并发与同步专题 (2)spinlock> <Li ...
- 线程间同步之 semaphore(信号量)
原文地址:http://www.cnblogs.com/yuqilin/archive/2011/10/16/2214429.html semaphore 可用于进程间同步也可用于同一个进程间的线程同 ...
- java之线程(线程的创建方式、java中的Thread类、线程的同步、线程的生命周期、线程之间的通信)
CPU:10核 主频100MHz 1核 主频 3GHz 那么哪一个CPU比较好呢? CPU核不是越多越好吗?并不一定.主频用于衡量GPU处理速度的快慢,举个例子10头牛运送货物快还是1架飞机运 ...
- java_线程、同步、线程池
线程 Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例 Thread类常用方法 构造方法 public Thread():分配一个新的线程 ...
- linux线程间同步方式总结梳理
线程间一般无需特别的手段进行通信,由于线程间能够共享数据结构,也就是一个全局变量能够被两个线程同时使用.只是要注意的是线程间须要做好同步! 使用多线程的理由: 1. 一个是和进程相比,它是一种非常&q ...
- Java线程:线程的同步-同步方法
Java线程:线程的同步-同步方法 线程的同步是保证多线程安全访问竞争资源的一种手段. 线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问 ...
- Linux并发与同步专题 (4) Mutex互斥量
关键词:mutex.MCS.OSQ. <Linux并发与同步专题 (1)原子操作和内存屏障> <Linux并发与同步专题 (2)spinlock> <Linux并发与同步 ...
- Linux并发与同步专题
并发访问:多个内核路径同时访问和操作数据,就有可能发生相互覆盖共享数据的情况,造成被访问数据的不一致. 临界区:访问和操作共享数据的代码段. 并发源:访问临界区的执行线程或代码路径. 在内核中产生并发 ...
- Linux并发与同步专题 (2)spinlock
关键词:wfe.FIFO ticket-based.spin_lock/spin_trylock/spin_unlock.spin_lock_irq/spin_lock_bh/spin_lock_ir ...
随机推荐
- Java11新特性!
Java11又出新版本了,我还在Java8上停着.不过这也挡不住我对他的热爱,忍不住查看了一下他的新性能,由于自己知识有限,只总结了以下八个特性: 1.本地变量类型推断 什么是局部变量类型推断? va ...
- GIL:全局解释器锁 VS 用户程序锁
既然有了GIL锁,CPython还要多线程干什么? ''' GIL:全局解释器锁的来历 四核:同一时刻真正有四个任务在运行,多核的意义在于此 单核:看上去是并发的,因为进行了上下文切换,单核永远是串行 ...
- solr 7.7 搭建和搜索
最近做了个solr搜索的demo, 用的是solr7.7,之前网上好多文章搭建solr都是5.5+tomcat.. 自己摆弄了下,发现solr7.7和5.5 的部分文件夹有些改动,没有深究原理也就没有 ...
- docker 安装 fastdfs
fastdfs 安装 //1.拉取镜像 docker pull morunchang/fastdfs //2.启动tracker docker run -d --name tracker --net= ...
- fastjson List转JSONArray以及JSONArray转List
1.fastjson List转JSONArrayList<T> list = new ArrayList<T>();JSONArray array= JSONArray.p ...
- Codeforces Round 1152 (div. 2)
奇差.ABC三题,排名400. 首先是策略问题. 由于第一眼看到D的时候感觉不太会做,于是去看E. 一看到E这不欧拉回路吗,可做可做, 于是--我不会欧拉回路! 手推.推了半天啥也没弄出来, 于是大概 ...
- git 入门(转)
1. good https://github.com/521xueweihan/git-tips Git的奇技淫巧 2.如果之前未使用过 Git,可以学习 Git 小白教程入门 3.思维导图
- 在Bootstrap开发框架的工作流模块中实现流程完成后更新资料状态处理
在开发查看流程表单明细的时候,在Web界面中,我们往往通过使用@RenderPage实现页面内容模块化的隔离,减少复杂度,因此把一些常用的如审批.撤销.会签.阅办等等的流程步骤都放到了通用处理的页面V ...
- js实现小功能 动态赋值
- PHP中高级进阶之路
纯自己总结,认为作为一个中高级的PHP程序员,应该必修的内容,以此鞭策自己,努力向着这个方向前进. 1. 技能自问 1) PHP7开始使用了吗?它的一些新特性? 2) 数据库分库分表的实现 3) My ...