八、Linux下线程的互斥

一、线程的互斥锁

为什么使用线程锁?
在多线程应用程序中,当多个线程共享相同的内存时,如同时访问一个变量时,需要确保每个线程看到一致的数据视图,即保证所有线程对数据的修改是一致的。
如下两种情况不存在不一致的问题:

  1. 每个线程使用的变量都是其他线程不会读取和修改的
  2. 变量是只读的

当一个线程在修改变量的值时,其他线程在读取这个变量时可能会得到一个不一致的值。
一个典型的例子是,在一个多线程程序中,两个及以上个线程对同一个变量i执行i++操作,结果得到的值并不如顺序执行所预期的那样。这就是线程间不同步的一个例子。
可以用程序修改变量值时所经历的三个步骤解释这个现象:

  1. 从内存单元读入寄存器
  2. 在寄存器中对变量操作(加/减1)
  3. 把新值写回到内存单元

不能预期以上三步骤在一个总线周期内完成,所以也就不能指望多线程程序如预期那样运行。

1. 互斥量

多线程程序中可能会存在数据不一致的情况,那么如何保证数据一致呢?可以考虑同一时间只有一个线程访问数据。互斥量(mutex)就是一把锁。
多个线程只有一把锁一个钥匙,谁上的锁就只有谁能开锁。当一个线程要访问一个共享变量时,先用锁把变量锁住,然后再操作,操作完了之后再释放掉锁,完成。
当另一个线程也要访问这个变量时,发现这个变量被锁住了,无法访问,它就会一直等待,直到锁没了,它再给这个变量上个锁,然后使用,使用完了释放锁,以此进行。
这个即使有多个线程同时访问这个变量,也好象是对这个变量的操作是顺序进行的。

2. 互斥量的初始化

互斥锁(mutex)是一种简单的加锁的方法来控制对共享资源的访问。

  • 在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行访问。
  • 若其他线程希望上锁一个已经被上了互斥锁的资源,则该线程挂起,直到上锁的线程释放互斥锁为止。
a. 静态分配
pthread_mutex mutex = PTHREAD_MUTEX_INITIALIZER;
b. 动态分配

互斥变量使用特定的数据类型:pthread_mutex_t,使用互斥量前要先初始化,使用的函数如下:

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

其中 pthread_mutex_t 是一个表示互斥量的联合体类型,函数的第一个参数是要初始化的互斥量,第二个参数是要设置的互斥量的属性,参数二为空则表示使用默认的属性。函数初始化互斥锁成功返回0,否则返回非0。

静态初始化的互斥量不需要销毁,而对于动态初始化的互斥量则需要销毁,其接口函数如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数为要销毁的互斥量,上面的初始化和销毁的函数执行成功返回 0,出错返回错误码。

对互斥量加锁解锁的函数如下:

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthreadd_mutex_t *mutex);

第一个函数 pthread_mutex_lock 对互斥量阻塞式加锁,如果互斥量已经上锁,则调用线程将则阻塞式等待,直到互斥量被解锁。pthread_mutex_unlock 用于解锁,如果不希望阻塞式的加锁,则使用第二个函数 pthread_mutex_trylock ,该函数尝试对互斥量加锁,如果互斥量未被上锁,则将互斥量锁住,否则,该函数返回 EBUSY。

3. 案例改进

atm_account.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>

/** 账户信息 */
typedef struct {
    int         code;       ///< 银行账户的编码
    double      balance;    ///< 账户余额
	/*
	 *建议互斥锁用来锁定一个账户(共享资源),和账户(共享资源)绑定在一起。
	 *尽量不要设置成全局变量,否则会出现一把锁上几百个账户,导致并发性能降低。
	 */
    pthread_mutex_t mutex;    ///< 定义一把互斥锁,用来对多线程操作的银行账户(共享资源)进行加锁
}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);

    return account;
}

/** 销毁账户 */
void atm_account_Destroy(atm_Account *account)
{
    if(NULL == account){
        return ;
    }

    pthread_mutex_destroy(&account->mutex);
    free(account);
}

/** 取款: 成功,则返回取款金额 */
double atm_account_Withdraw(atm_Account *account, double amt)
{
    if(NULL == account) {
        return 0.0;
    }

    /** 对共享资源(账户进行加锁) */
    pthread_mutex_lock(&account->mutex);

    if(amt < 0 || amt > account->balance) {
        pthread_mutex_unlock(&account->mutex);
        return 0.0;
    }

    double balance_tmp = account->balance;
    sleep(1);
    balance_tmp -= amt;
    account->balance = balance_tmp;

    pthread_mutex_unlock(&account->mutex);

    return amt;
}

/** 存款: 返回存款的金额 */
double atm_account_Desposit(atm_Account *account, double amt)
{
    if(NULL == account){
        return 0.0;
    }

    /** 对共享资源(账户进行加锁) */
    pthread_mutex_lock(&account->mutex);

    if(amt < 0){
        pthread_mutex_unlock(&account->mutex);
        return 0.0;
    }

    double balance_tmp = account->balance;
    sleep(1);
    balance_tmp += amt;
    account->balance = balance_tmp;

    pthread_mutex_unlock(&account->mutex);

    return amt;
}

/** 查看账户余额 */
double atm_account_BalanceGet(atm_Account *account)
{
    if(NULL == account){
        return 0.0;
    }

    /** 对共享资源(账户进行加锁) */
    pthread_mutex_lock(&account->mutex);

    double balance_tmp = account->balance;
    pthread_mutex_unlock(&account->mutex);

    return balance_tmp;
}

从运行的结果可知,对共享资源加锁后,对资源的访问就存在互斥的关系,将不会出现上述错误,男孩操作账户对共享资源操作时,女孩是不能操作共享资源的。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值