四十三、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 ...
随机推荐
- Hadoop Yarn配置项 yarn.nodemanager.resource.local-dirs探讨
1. What is the recommended value for "yarn.nodemanager.resource.local-dirs"? We only have ...
- SQL FORMAT() 函数
FORMAT() 函数 FORMAT 函数用于对字段的显示进行格式化. SQL FORMAT() 语法 SELECT FORMAT(column_name,format) FROM table_nam ...
- Python 隔离环境 virtualenv
1) 安装 $ sudo pip3 install virtualenv 2) 创建并进入工程目录,例如 myproject $ mkdir myproject $ cd myproject 3) 在 ...
- C# 使用DES对字符串进行加密
1.DES加密是属于对称加密,加密和解密使用的密钥必须要保持一致,且必须为8位,使用前首先添加引用: 2.逻辑实现代码如下:
- 转 HttpClient 设置连接超时时间
要: HttpClient 4.5版本升级后,设置超时时间的API又有新的变化,请大家关注. HttpClient升级到4.5版本后,API有很多变化,HttpClient 4之后,API一直没有太稳 ...
- PS图片后期之超简易造光调色方法
技法是死的,而人是活的,说的简单一点就是我们要学会开拓一下思维,调色的方法并不是只有[可选颜色]而已. 在修片之前,我们先要学会分析,在拍摄这一组照片时我希望有一种夕阳的光穿透晒在脸庞的感觉,而左边的 ...
- 计算机网络基础知识-OSI七层协议模型
一.物理层 物理层主要规定了物理设备的标准,如网线的类型.光纤的接口类型.各种传输介质的传输速率,物理层的数据以比特流(二进制)的形式存在,传输时将比特流转化为电流强弱,达到目的地之后再转化为比特流. ...
- 神经网路-SGD-1
SGD神经网络以及python中实现 1.SGD(stochastic gradient descend):<1>数据抽取:<2>计算梯度;<3>参数更新:< ...
- wepy项目创建
全局安装wepy npm install wepy-cli -g 创建项目 wepy init standard mywepy 安装依赖 npm install 实时编译 wepy build --w ...
- Shell命令-文件及目录操作之cp、find
文件及目录操作 - cp.find 1.cp:复制文件或目录 cp命令的功能说明 cp命令用于复制文件或目录. cp命令的语法格式 cp [OPTION]... SOURCE... DIRECTORY ...