zoukankan      html  css  js  c++  java
  • 第9章 线程编程(7)_线程同步4:信号量

    5.5 线程信号量

    5.5.1 信号量函数简介

    (1)信号量(semaphore)从本质上是一个非负整数计数器,是共享资源的数目,通常被用来控制对共享资源的访问。

    (2)信号量可以实现线程的同步和互斥

    (3)通过sem_post()sem_wait()函数对信号量进行加减操作,从而解决线程的同步和互斥。

    (4)信号量数据类型sem_t

    5.5.2 信号量的操作

    (1)信号量的创建和销毁

    头文件

    #include <semaphore.h>

    函数

    int sem_init(sem_t* sem, int pshared, unsigned value); //初始化

    int sem_destroy(sem_t* sem); //销毁信号量

    返回值

    成功返回0,否则返回错误编号

    参数

    sem:信号量指针

    pshared:是否在进程间共享的标志,0为不共享,1为共享

    value:信号量的初始值

    (2)信号量的加和减操作

    头文件

    #include <semaphore.h>

    函数

    int sem_post (sem_t* sem); //增加信号量的值

    int sem_wait(sem_t* sem);  //减少信号量的值

    int sem_trywait(sem_t* sem); //sem_wait的非阻塞版本

    返回值

    成功返回0,否则返回错误编号

    备注

    ①调用sem_post一次,信号量作加1操作。

    ②调用sem_wait一次,信号量作减1操作。

    ③当线程调用sem_wait后,若信号量的值小于0,则线程阻塞只有其它线程在调用sem_post对信号量作加操作后并且其值大小或等于0时,阻塞的线程才能继续运行

    【编程实验】三个线程交替输出

    //sem_test.c

    #include <semaphore.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    //三个线程按a、b、c的顺序交替输出
    
    //定义线程信号量
    sem_t semA;
    sem_t semB;
    sem_t semC;
    
    #define LOOP 5 //循环次序
    
    void* a_fn(void* arg)
    {
        int i=0;
        for(; i<LOOP; i++){
            sem_wait(&semA);
            printf("%d thread a(0x%lx) is running
    ", i+1, pthread_self());
            sem_post(&semB); //唤醒b线程
        }
    
        return (void*)0;
    }
    
    void* b_fn(void* arg)
    {
        int i=0;
        for(; i<LOOP; i++){
            sem_wait(&semB);
            printf("%d thread b(0x%lx) is running
    ", i+1, pthread_self());
            sem_post(&semC); //唤醒c线程
        }
    
        return (void*)0;
    }
    
    void* c_fn(void* arg)
    {
        int i=0;
        for(; i<LOOP; i++){
            sem_wait(&semC);
            printf("%d thread c(0x%lx) is running
    ", i+1, pthread_self());
            sem_post(&semA); //唤醒a线程
        }
    
        return (void*)0;
    }
    
    int main(void)
    {
        pthread_t a, b, c;
    
        //初始化线程信号量
        sem_init(&semA, 0, 1); //信号灯为1,表示a先运行
        sem_init(&semB, 0, 0); //b、c线程先暂停
        sem_init(&semC, 0, 0);
    
        pthread_create(&a, NULL, a_fn, (void*)0);
        pthread_create(&b, NULL, b_fn, (void*)0);
        pthread_create(&c, NULL, c_fn, (void*)0);
    
        pthread_join(a, NULL);
        pthread_join(b, NULL);
        pthread_join(c, NULL);
    
        sem_destroy(&semA);
        sem_destroy(&semB);
        sem_destroy(&semC);
    
        return 0;
    }
    /*输出结果:
    1 thread a(0xb7779b70) is running
    1 thread b(0xb6d78b70) is running
    1 thread c(0xb6377b70) is running
    2 thread a(0xb7779b70) is running
    2 thread b(0xb6d78b70) is running
    2 thread c(0xb6377b70) is running
    3 thread a(0xb7779b70) is running
    3 thread b(0xb6d78b70) is running
    3 thread c(0xb6377b70) is running
    4 thread a(0xb7779b70) is running
    4 thread b(0xb6d78b70) is running
    4 thread c(0xb6377b70) is running
    5 thread a(0xb7779b70) is running
    5 thread b(0xb6d78b70) is running
    5 thread c(0xb6377b70) is running
    */

    【编程实验】PV操作:银行帐户

    (1)PV原语操作:P操作——减;V操作:加;

    (2)sem_post 信号灯加1操作 ==>V(1)操作;

         sem_wait 信号灯减1操作 ==>P(1)操作;

    //account.h

    #ifndef __ACCOUNT_H__
    #define __ACCOUNT_H__
    #include <pthread.h>
    #include <semaphore.h>
    
    typedef struct
    {
        int      code;    //帐号
        double   balance; //余额
    
        //使用信号量,用来对多线程操作的银行帐户(共享资源)进行加锁保护。
        /*
         *建议信号量和一个帐户绑定。尽量不设置成全局变量,否则可能出更一
         *锁去锁定多个帐户,导致并发性能降低。
         */
        sem_t sem; //定义线程信号量
    }Account;
    
    //创建账户
    extern Account* create_account(int code, double balance);
    //销毁帐户
    extern void destroy_account(Account* a);
    //取款
    extern double withdraw(Account* a, double amt); //amt == amount
    //存款
    extern double deposit(Account* a, double amt);
    //查看帐户余额
    extern double get_balance(Account* a);
    
    #endif  //__ACCOUNT_H__

    //account.c

    #include "account.h"
    #include <malloc.h>
    #include <string.h>
    #include <assert.h>
    
    //创建账户
    Account* create_account(int code, double balance)
    {
        Account* ret = (Account*)malloc(sizeof(Account));
        assert(ret != NULL);
    
        ret->code = code;
        ret->balance = balance;
        
        //对信号量进行初始化
        sem_init(&ret->sem, 0, 1);//1个信号灯
        return ret;
    }
    
    //销毁帐户
    void destroy_account(Account* a)
    {
        assert( a != NULL);
        
        //销毁信号量
        sem_destroy(&a->sem);
    
        free(a);
    }
    
    //取款
    double withdraw(Account* a, double amt) //amt == amount
    {
        assert(a != NULL);
    
        //等待信号量:P(1)操作
        sem_wait(&a->sem);
    
        if((amt < 0) || (amt > a->balance)){
            //V(1)操作
            sem_post(&a->sem);
            return 0.0;
        }
    
        double balance = a->balance; //先取余额
    
        sleep(1); //为模拟多线程下可能出现的问题
    
        balance -= amt;
        a->balance = balance; //更新余额。在读取余额和更新余额之间有
                              //故意留出“时间窗口”。
        
        //V(1)操作
        sem_post(&a->sem);
     
        return amt;    
    }
    
    //存款
    double deposit(Account* a, double amt)
    {
        assert(a != NULL);
    
        if(amt < 0){
            return 0.0;
        }
        
        //P(1)操作
        sem_wait(&a->sem);//等待信号量
    
        double balance = a->balance; //先取余额
    
        sleep(1); //为模拟多线程下可能出现的问题
    
        balance += amt;
        a->balance = balance; //更新余额。
        
        //V(1)操作
        sem_post(&a->sem); //通知操作完毕
     
        return amt;    
    }
    
    //查看帐户余额
    double get_balance(Account* a)
    {
        assert(a != NULL);
        
        //P(1)操作
        sem_wait(&a->sem);
        double balance = a->balance;
        //V(1)操作
        sem_post(&a->sem);
     
        return balance;
    }

    //account_test.c

    #include "account.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    //#include <string.h> //for strcpy
    
    typedef struct
    {
        char name[20];
        Account* account;
        double amt;
    }OperArg;
    
    //定义取款操作的线程函数
    void* withdraw_fn(void* arg)
    {
        OperArg* oa = (OperArg*)arg;
       
        double amt = withdraw(oa->account, oa->amt);
        printf("%s(0x%lx) withdraw %f from account(%d)
    ", 
                  oa->name,pthread_self(), amt, oa->account->code);
    
        return (void*)0;
    }
    
    //定义存款操作的线程函数
    void* deposit_fn(void* arg)
    {
        OperArg* oa = (OperArg*)arg;
        
        double amt = deposit(oa->account, oa->amt);
        printf("%s(0x%lx) deposit %f from account(%d)
    ", 
                  oa->name,pthread_self(), amt, oa->account->code);
    
        return (void*)0;
    }
    
    int main(void)
    {
        int err = 0;
        pthread_t boy, girl;
    
        Account* a = create_account(100001, 10000);
        OperArg o1 = {"boy",  a, 10000}; //strcpy(o1.name, "boy");
        OperArg o2 = {"girl", a, 10000};
        
        //启动两个子线程(boy和girl线程)同时去操作同一个银行帐户
        if((err = pthread_create(&boy, NULL, withdraw_fn, (void*)&o1)) != 0){
            perror("pthread_create error");
        }
    
        if((err = pthread_create(&girl, NULL, withdraw_fn, (void*)&o2)) != 0){
            perror("pthread_create error");
        }
        
        pthread_join(boy, NULL);
        pthread_join(girl, NULL);
    
        //查看余额
        printf("account balance: %f
    ", get_balance(a));
        
        destroy_account(a);
        
        return 0;
    }

    【编程实验】PV操作:计算1+2+3+…+100

    //pthread_sem_cal.c

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <semaphore.h>
    
    /*一个线程负责计算,一个线程获取结果*/
    
    typedef struct
    {
        int      res; //计算结果
        sem_t    sem;
    }Result;
    
    //计算并将结果放置在Result中的线程函数
    void*  set_fn(void* arg)
    {
        Result* r = (Result*)arg;
    
        int i = 1;
        int sum = 0;
        for(; i<=100; i++){
            sum += i;
        }
    
        //将计算结果放入Result中
        r->res = sum;
    
        //通知获取结果的线程,计算完毕!
        sem_post(&r->sem);
    
        return (void*)0;
    }
    
    //获取结果的线程运行函数
    void* get_fn(void* arg)
    {
        Result* r = (Result*)arg;
        
        sem_wait(&r->sem); //等待计算结果
    
        //输出结果
        int res = r->res;
        printf("0x%lx get sum is %d
    ", pthread_self(), res);
    
        return (void*)0;
    }
    
    int main(void)
    {
        int err = 0;
        pthread_t  cal, get;
        Result  r;
        sem_init(&r.sem, 0, 0); //信号灯初始为0,让读取线程进入等待状态
    
        //启动获取结果的线程
        if((err = pthread_create(&get, NULL, get_fn, (void*)&r)) !=0 ){
            perror("pthread create error");
        }
    
        //启动计算的线程
        if((err = pthread_create(&cal, NULL, set_fn, (void*)&r)) !=0 ){
            perror("pthread create error");
        }
    
        //等待子线程结束
        pthread_join(cal, NULL);
        pthread_join(get, NULL);
    
        //销毁信号量
        sem_destroy(&r.sem);
    
        return 0;
    }
    /*输出结果
     0xb6df1b70 get sum is 5050
     */
  • 相关阅读:
    25 自动类型转换
    24 枚举Enum类
    23 Collection集合常用方法讲解
    Eclipse 快捷键
    21 泛型
    20 IO流(十七)——Commons工具包,FileUtils(二)——copy方法
    19 IO流(十六)——Commons工具包,FileUtils(一)
    18 IO流(十五)——RandomAccessFile随机访问文件及使用它进行大文件切割的方法
    CentOS6.5-6.9安装 docker
    linux开启端口
  • 原文地址:https://www.cnblogs.com/5iedu/p/6424376.html
Copyright © 2011-2022 走看看