四十三、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 ...
随机推荐
- Flafka: Apache Flume Meets Apache Kafka for Event Processing
The new integration between Flume and Kafka offers sub-second-latency event processing without the n ...
- [LeetCode] 16. 最接近的三数之和
题目链接:https://leetcode-cn.com/problems/3sum-closest/ 题目描述: 给定一个包括 n 个整数的数组 nums 和 一个目标值 target.找出 num ...
- 微信小程序开发 (资料汇总,谁还没被坑过?希望助你绕过一些坑)
最近帮人家做一个微信小程序,刚好想熟悉一下.由于牵扯到多用户使用系统,以及数据共享,所以自然架构选择了,客户端和服务器的方式. 后台服务器是windows server,后台程序是.Net WebA ...
- 小程序蓝牙BLE——自动连接设备(手环)
了解小程序蓝牙API: /** *蓝牙API: * 1.初始化蓝牙(判断蓝牙是否可用):openBluetoothAdapter * 2.获取蓝牙设备状态(蓝牙是否打开):getBluetoothAd ...
- Static Sushi AtCoder - 4118 (技巧枚举)
Problem Statement "Teishi-zushi", a Japanese restaurant, is a plain restaurant with only o ...
- 利用Python中SocketServer 实现客户端与服务器间非阻塞通信
利用SocketServer模块来实现网络客户端与服务器并发连接非阻塞通信 版权声明 本文转自:http://blog.csdn.net/cnmilan/article/details/9664823 ...
- MyBatis基础:MyBatis关联查询(4)
1. MyBatis关联查询简介 MyBatis中级联分为3中:association.collection及discriminator. ◊ association:一对一关联 ◊ collecti ...
- codeforces645B
Mischievous Mess Makers CodeForces - 645B It is a balmy spring afternoon, and Farmer John's n cows a ...
- ES-6常用语法和Vue初识
一.ES6常用语法 1.变量的定义 1. 介绍 ES6以前 var关键字用来声明变量,无论声明在何处都存在变量提升这个事情,会提前创建变量. 作用域也只有全局作用域以及函数作用域,所以变量会提升在函数 ...
- Virtual Box虚拟机安装Ubuntu16.04以及整理的一些基本操作
事先声明,参考自:https://www.cnblogs.com/wyt007/p/9856290.html 撰写此文,纯属是为了便利以后换电脑重装. 转载请注明地址:https://www.cnbl ...