zoukankan      html  css  js  c++  java
  • 四十三、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 }

      编译运行:

      

  • 相关阅读:
    SPOJ 1812 Longest Common Substring II(后缀自动机)(LCS2)
    HDU 4441 Queue Sequence(优先队列+Treap树)(2012 Asia Tianjin Regional Contest)
    HDU 4433 locker(DP)(2012 Asia Tianjin Regional Contest)
    HDU 4431 Mahjong(枚举+模拟)(2012 Asia Tianjin Regional Contest)
    NavigationBar的简单设置
    Android如何设置标题栏的高度
    android 在标题栏加上按钮
    MediaRecorder类介绍
    Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
    2016/1/7 改 百文百鸡 水仙花数 百马百担
  • 原文地址:https://www.cnblogs.com/kele-dad/p/10264116.html
Copyright © 2011-2022 走看看