四十三、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 ...
随机推荐
- 《常见排序算法--PHP实现》
原文地址: 本文地址:http://www.cnblogs.com/aiweixiao/p/8202360.html Original 2018-01-02 关注 微信公众号 程序员的文娱情怀 1.概 ...
- RabbitMQ广播:direct模式
一. 消息的广播需要exchange:exchange是一个转发器,其实把消息发给RabbitMQ里的exchange fanout: 所有bind到此exchange的queue都可以接收消息,广播 ...
- bzoj3122 [SDOI2013]随机数生成器
bzoj3122 [SDOI2013]随机数生成器 给定一个递推式, \(X_i=(aX_{i-1}+b)\mod P\) 求满足 \(X_k=t\) 的最小整数解,无解输出 \(-1\) \(0\l ...
- php curl cookie 读写
普通 curl post 请求 public static function curlPost($url, $post_fields = array(), $timeout = 5) { $timeo ...
- flask(二)之Jinja2模板与Flask-WTF
01-文档 官方文档:http://docs.jinkan.org/docs/jinja2/ 02-基本语义 Jinja2做构成的模板文件中,文本内容大致可以分成几个种类.比如特殊文本(不进行转义,比 ...
- react混合开发APP,资源分享
第一个: 链接:https://pan.baidu.com/s/1KdIs8EUcB9YTuK9VW1dC7g 密码:b68m 第二个: 链接:https://pan.baidu.com/s/1mi7 ...
- ABP中的拦截器之AuditingInterceptor
在上面两篇介绍了ABP中的ValidationInterceptor之后,我们今天来看看ABP中定义的另外一种Interceptor即为AuditingInterceptor,顾名思义就是一种审计相关 ...
- Linux 系统中五笔输入法有些字打不出来(已解决)
最近在使用CentOS7 桌面版本,在用五笔打字时,有些字打不出来,比如“覆盖”.但是在WIN下能打出来. 从网上查找原因,原来是需要改成GBK字符集.方法如下: 修改文件 vim /usr/shar ...
- 使用jenkins进行前端项目自动部署
前面的话 后端的nodeJS项目可以使用pm2进行自动部署,由于前端项目打包后是静态资源,不需要进程守护.一般地,前端项目使用jenkins来进行自动部署,包括打包.测试等一系列流程.本文将详细介绍j ...
- AtCoder Beginner Contest 122 D - We Like AGC(DP)
题目链接 思路自西瓜and大佬博客:https://www.cnblogs.com/henry-1202/p/10590327.html#_label3 数据范围小 可直接dp f[i][j][a][ ...