线程互斥锁

当不同线程同时访问同一个数据时,如果不加任何处理的话,就可能出现数据错乱。用一个例子说明:

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>

int num = 10;

void* call_back1(void* arg)
{
    num += 1;

    /* 假设这里执行了很多语句,用了一段时间 */
    sleep(2);

    printf("thread1: num=%d\n",num);
    return NULL;
}

void* call_back2(void* arg)
{
    /* 让线程1先改变 num 的值 */
    sleep(1);

    num -= 1;
    printf("thread2: num=%d\n",num);

    return NULL;
}

int main()
{
    pthread_t pthrd1,pthrd2;

    pthread_create(&pthrd1,NULL,call_back1,NULL);
    pthread_create(&pthrd2,NULL,call_back2,NULL);
    pthread_join(pthrd1,NULL);
    pthread_join(pthrd2,NULL);
    printf("main: num=%d\n",num);

    return 0;
}

程序的本意是线程一对 num 执行加一操作并输出 num,线程二对 num 进行减一操作并输出 num。然而由于线程以加一过后执行了很多其他操作后才输出 num,但是在输出之前,线程二已经对 num 的值进行了减一操作,所以线程一输出的值是 10(10 --- 11 --- 10).

 lingyun@manjaro  ~/Document/CppCode/study  gcc study.cpp -o study -lpthread -g
 lingyun@manjaro  ~/Document/CppCode/study  ./study                            
thread2: num=10
thread1: num=10
main: num=10
 lingyun@manjaro  ~/Document/CppCode/study  

 可见,如果不做任何处理,就会出现数据错乱的情况。我们需要用互斥锁来解决这个问题,互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。
互斥锁的操作流程如下:

  •     在访问共享资源后临界区域前,对互斥锁进行加锁。
  •     在访问完成后释放互斥锁导上的锁。
  •     对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

函数说明

#include<pthread.h>

/* 1. 初始化一个互斥锁 */
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
/* 参数:
    mutex:互斥锁地址。类型是 pthread_mutex_t 。
    attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。
*/
/* 可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:*/
        pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
/* 这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。*/
/*
返回值:
    成功:0,成功申请的锁默认是打开的。
    失败:非 0 错误码
 */


/* 2. 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。 */
int pthread_mutex_lock(pthread_mutex_t *mutex);
/* 参数:
    mutex:互斥锁地址。
*/
/* 返回值:
    成功:0
    失败:非 0 错误码
*/

/* 调用该函数时,若互斥锁未加锁,则上锁,返回 0;若互斥锁已加锁,则函数直接返回失败,即 EBUSY。*/
int pthread_mutex_trylock(pthread_mutex_t *mutex);


/* 3. 对指定的互斥锁解锁。 */
int pthread_mutex_unlock(pthread_mutex_t * mutex);
/* 参数:
    mutex:互斥锁地址。
*/
/* 返回值:
    成功:0
    失败:非 0 错误码
*/


/* 4. 销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。 */
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/* 参数:
    mutex:互斥锁地址。
*/
/* 返回值:
    成功:0
    失败:非 0 错误码
*/

用互斥锁优化上面的例子:

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>

int num = 10;

/* 定义一个互斥锁 */
pthread_mutex_t mutex;

void* call_back1(void* arg)
{
    /* 上锁 */
    pthread_mutex_lock(&mutex);

    num += 1;

    /* 假设这里执行了很多语句,用了一段时间 */
    sleep(2);

    printf("thread1: num=%d\n",num);

    /* 解锁 */
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* call_back2(void* arg)
{
    /* 让线程1先改变 num 的值 */
    sleep(1);

    /* 上锁 */
    pthread_mutex_lock(&mutex);

    num -= 1;
    printf("thread2: num=%d\n",num);

    /* 解锁 */
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main()
{
    pthread_t pthrd1,pthrd2;

    /* 初始化互斥锁的两种方式 */
    mutex = PTHREAD_MUTEX_INITIALIZER;
    //pthread_mutex_init(&mutex,NULL);

    pthread_create(&pthrd1,NULL,call_back1,NULL);
    pthread_create(&pthrd2,NULL,call_back2,NULL);
    pthread_join(pthrd1,NULL);
    pthread_join(pthrd2,NULL);
    printf("main: num=%d\n",num);

    /* 销毁互斥锁 */
    pthread_mutex_destroy(&mutex);

    return 0;
}

执行可见,这次线程一正长输出了加一后的 num 值 11。当线程一执行 pthread_mutex_lock(&mutex) 后,线程二执行到 pthread_mutex_lock(&mutex) 时就会阻塞。等待线程一执行pthread_mutex_unlock(&mutex) 后才能继续执行。

 lingyun@manjaro  ~/Document/CppCode/study  gcc study.cpp -o study -lpthread -g
 lingyun@manjaro  ~/Document/CppCode/study  ./study                            
thread1: num=11
thread2: num=10
main: num=10
 lingyun@manjaro  ~/Document/CppCode/study  

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值