四十三、Linux 线程——线程同步之线程信号量
43.1 信号量
43.1.1 信号量介绍
- 信号量从本质上是一个非负整数计数器,是共享资源的数目,通常被用来控制对共享资源的访问
- 信号量可以实现线程的同步和互斥
- 通过 sem_post() 和 sem_wait() 函数对信号量进行加减操作从而解决线程的同步和互斥
- 信号量数据类型:sem_t
43.1.2 信号量创建和销毁
1 #include <semaphore.h> 2 int sem_init(sem_t *sem, int pshared, unsigned value); 3 int sem_destroy(sem_t *sem);
- 函数参数:
- sem:信号量指针
- pshared:是否在进程间共享的标识,0 为不共享,1 为共享
- value:信号量的初始值
- 返回值:成功,返回 0;出错,返回错误编号
43.1.3 信号量的加和减操作
1 #include <semaphore.h> 2 /** 增加信号量的值 */ 3 int sem_post(sem_t *sem); 4 5 /** 非阻塞版本,减少信号量的值 */ 6 int sem_wait(sem_t *sem); 7 8 /** 阻塞版本,减少信号量的值 */ 9 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
1 #include <semaphore.h> 2 #include <pthread.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 /** 定义线程信号梁 */ 7 sem_t sem1; 8 sem_t sem2; 9 10 void *a_fn(void *arg) 11 { 12 sem_wait(&sem1); 13 printf("thread a running\n"); 14 return (void *)0; 15 } 16 17 void *b_fn(void *arg) 18 { 19 sem_wait(&sem2); 20 sem_post(&sem1); ///< 释放线程 a 21 printf("thread b running\n"); 22 return (void *)0; 23 } 24 25 void *c_fn(void *arg) 26 { 27 sem_post(&sem2); ///<释放线程 b,对线程信号量 sem2 做 +1 操作,让阻塞的线程 b 运行 28 printf("thread c running\n"); 29 return (void *)0; 30 } 31 32 int main(void) 33 { 34 pthread_t a, b ,c; 35 36 /** 线程信号量初始化 */ 37 sem_init(&sem1, 0, 0); 38 sem_init(&sem2, 0, 0); 39 40 pthread_create(&a, NULL, a_fn, (void *)0); 41 pthread_create(&b, NULL, b_fn, (void *)0); 42 pthread_create(&c, NULL, c_fn, (void *)0); 43 44 pthread_join(a, NULL); 45 pthread_join(b, NULL); 46 pthread_join(c, NULL); 47 48 sem_destroy(&sem1); 49 sem_destroy(&sem2); 50 51 return 0; 52 }
编译运行结果:
43.2.2 PV操作银行账户
-
- P 操作:减,如减 1 操作 sem_wait()
- V 操作:加,加 1 操作 sem_post()
atm_count.h
1 #ifndef __ATM_ACCOUNT_H__ 2 #define __ATM_ACCOUNT_H__ 3 4 #include <math.h> 5 #include <malloc.h> 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <pthread.h> 11 #include <semaphore.h> 12 13 /** 账户信息 */ 14 typedef struct { 15 int code; ///< 银行账户的编码 16 double balance; ///< 账户余额 17 18 /** 建议互斥锁用来锁定一个账户(共享资源),和账户绑定再一起, 19 * 尽量不设置成全局变量,否则可能出现一把锁去锁几百个账户,导致并发性能降低 */ 20 //pthread_mutex_t mutex; ///< 定义一把互斥锁,用来对多线程操作的银行账户(共享资源)进行加锁 21 22 //pthread_rwlock_t rwlock; ///<定义读写锁 23 24 //定义线程信号量 25 sem_t sem; 26 }atm_Account; 27 28 /** 创建账户 */ 29 extern atm_Account *atm_account_Create(int code, double balance); 30 /** 销毁账户 */ 31 extern void atm_account_Destroy(atm_Account *account); 32 /** 取款 */ 33 extern double atm_account_Withdraw(atm_Account *account, double amt); 34 /** 存款 */ 35 extern double atm_account_Desposit(atm_Account *account, double amt); 36 /** 查看账户余额 */ 37 extern double atm_account_BalanceGet(atm_Account *account); 38 39 #endif
atm_account.c
1 #include "atm_account.h" 2 3 /** 创建账户 */ 4 atm_Account *atm_account_Create(int code, double balance) 5 { 6 atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account)); 7 if(NULL == account) { 8 return NULL; 9 } 10 11 account->code = code; 12 account->balance = balance; 13 14 /** 对互斥锁进行初始化 */ 15 //pthread_mutex_init(&account->mutex, NULL); 16 17 /** 初始化读写锁 */ 18 //pthread_rwlock_init(&account->rwlock, NULL); 19 20 /** 初始化线程信号量 */ 21 sem_init(&account->sem, 0, 1); 22 23 return account; 24 } 25 26 /** 销毁账户 */ 27 void atm_account_Destroy(atm_Account *account) 28 { 29 if(NULL == account){ 30 return ; 31 } 32 33 //pthread_mutex_destroy(&account->mutex); 34 //pthread_rwlock_destroy(&account->rwlock); ///< 销毁读写锁 35 36 /** 销毁线程信号量 */ 37 sem_destroy(&account->sem); 38 free(account); 39 } 40 41 /** 取款: 成功,则返回取款金额 */ 42 double atm_account_Withdraw(atm_Account *account, double amt) 43 { 44 if(NULL == account) { 45 return 0.0; 46 } 47 48 /** 对共享资源(账户进行加锁) */ 49 //pthread_mutex_lock(&account->mutex); 50 //pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁 51 /** P(1) 操作 */ 52 sem_wait(&account->sem); 53 54 if(amt < 0 || amt > account->balance) { 55 //pthread_mutex_unlock(&account->mutex); 56 //pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 57 58 /** V(1) 操作 */ 59 sem_post(&account->sem); 60 return 0.0; 61 } 62 63 double balance_tmp = account->balance; 64 sleep(1); 65 balance_tmp -= amt; 66 account->balance = balance_tmp; 67 68 //pthread_mutex_unlock(&account->mutex); 69 //pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 70 71 /** V(1) 操作 */ 72 sem_post(&account->sem); 73 return amt; 74 } 75 76 /** 存款: 返回存款的金额 */ 77 double atm_account_Desposit(atm_Account *account, double amt) 78 { 79 if(NULL == account){ 80 return 0.0; 81 } 82 83 /** 对共享资源(账户进行加锁) */ 84 //pthread_mutex_lock(&account->mutex); 85 //pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁 86 87 /** P(1) 操作 */ 88 sem_wait(&account->sem); 89 90 if(amt < 0){ 91 //pthread_mutex_unlock(&account->mutex); 92 //pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 93 94 /** V(1) 操作 */ 95 sem_post(&account->sem); 96 return 0.0; 97 } 98 99 double balance_tmp = account->balance; 100 sleep(1); 101 balance_tmp += amt; 102 account->balance = balance_tmp; 103 104 //pthread_mutex_unlock(&account->mutex); 105 //pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 106 107 /** V(1) 操作 */ 108 sem_post(&account->sem); 109 return amt; 110 } 111 112 /** 查看账户余额 */ 113 double atm_account_BalanceGet(atm_Account *account) 114 { 115 if(NULL == account){ 116 return 0.0; 117 } 118 119 /** 对共享资源(账户进行加锁) */ 120 //pthread_mutex_lock(&account->mutex); 121 //pthread_rwlock_rdlock(&account->rwlock); ///< 上读锁 122 123 /** P(1) 操作 */ 124 sem_wait(&account->sem); 125 126 double balance_tmp = account->balance; 127 //pthread_mutex_unlock(&account->mutex); 128 //pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁 129 130 /** V(1) 操作 */ 131 sem_post(&account->sem); 132 133 return balance_tmp; 134 }
编译运行结果:
43.2.3 利用线程信号量实现线程之间的同步
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <unistd.h> 5 #include <semaphore.h> 6 7 /** 两个线程定义的共享资源 */ 8 typedef struct { 9 int res; 10 sem_t sem; 11 }Result; 12 13 14 /** 计算并将结果放置在 Result 中的线程运行函数 */ 15 void *set_fn(void *arg) 16 { 17 Result *r = (Result *)arg; 18 int i = 0; 19 int sum = 0; 20 21 for(; i <= 100; i++){ 22 sum += i; 23 } 24 25 /** 将结果放置到 Result 中 */ 26 r->res = sum; 27 28 sem_post(&r->sem); 29 return (void *)0; 30 } 31 32 /** 获得结果的线程运行函数 */ 33 void *get_fn(void *arg) 34 { 35 Result *r = (Result *)arg; 36 37 sem_wait(&r->sem); 38 /** 去获取计算结果 */ 39 int res = r->res; 40 printf("0x%lx get sum is %d\n", pthread_self(), res); 41 42 return (void *)0; 43 } 44 45 int main(void) 46 { 47 int err; 48 pthread_t cal, get; 49 50 Result r; 51 sem_init(&r.sem, 0, 0); 52 /** 启动获取结果的线程 */ 53 if((err = pthread_create(&get, NULL, get_fn, (void *)&r)) != 0){ 54 perror("pthread create error"); 55 } 56 57 /** 启动计算结果的线程 */ 58 if((err = pthread_create(&cal, NULL, set_fn, (void *)&r)) != 0){ 59 perror("pthread create error"); 60 } 61 62 pthread_join(cal, NULL); 63 pthread_join(get, NULL); 64 sem_destroy(&r.sem); 65 return 0; 66 }
编译运行: