四十三、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
");
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
");
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
");
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
", 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 }

  编译运行:

  

原文地址:https://www.cnblogs.com/kele-dad/p/10264116.html