pthread_mutex_t & pthread_cond_t 总结

pthread_mutex_t & pthread_cond_t 总结一、多线程并发1.1 多线程并发引起的问题我们先来看如下代码:
#include <stdio.h>#include <pthread.h>#include <unistd.h>#define MAX 1E7int giNum = 0;void *func1(void *arg){    int i;    for (i = 1; i <= MAX; i++)    {        giNum++;    }    return NULL;}void *func2(void *arg){    int i;    for (i = 1; i <= MAX; i++)    {        giNum++;    }    return NULL;}int main(){    pthread_t th1;    pthread_create(&th1, NULL, func1, NULL);    pthread_t th2;    pthread_create(&th2, NULL, func2, NULL);    pthread_join(th1, NULL);    pthread_join(th2, NULL);    printf("giNum = %d\n", giNum);    return 0;}代码的内容很简单:

  1. 创建了两个子线程 th1、th2
  2. 两个子线程分别执行giNum++操作
  3. 最后输出giNum的值
直观地看过去:
  • th1 运行时giNum++要执行 \(10^7\) 次
  • th2 运行时giNum++也要执行 \(10^7\) 次
似乎计算得到的最后 giNum 应该是 \(2\times10^7\) 。但实际上是这样的吗?让我们来看一下运行结果:
pthread_mutex_t &amp; pthread_cond_t 总结

文章插图
多次运行,你会发现,仅有一次(甚至没有)结果是正确的 。
1.2 知根知底上述代码得到的结果为什么不如顺序执行所预期的那样呢?可以用程序修改变量值时所经历的三个步骤解释这个现象:
  • 从内存单元读入寄存器
  • 在寄存器中对变量操作(加/减1)
  • 把新值写回到内存单元
即当我们当我们执行giNum++时 , 底层发生的事件其实是:
  1. 内存中读取 giNum;
  2. 将 giNum++;
  3. 将 giNum 写入到内存 。
这不是一个原子化操作 , 当两个线程交错运行的时候,很容易发生结果的丢失 。因此最后的结果肯定是要 \(\leq 2\times10^7\) 的 。这种情况有种专有名词,叫 race condition 。为了解决这个问题,我们可以「加锁」 。
二、线程锁2.1 互斥量多线程程序中可能会存在数据不一致的情况,那么如何保证数据一致呢?可以考虑同一时间只有一个线程访问数据 。
而互斥量(mutex)就是一把锁 。多个线程只有一把锁一个钥匙 , 谁上的锁就只有谁能开锁 。当一个线程要访问一个共享变量时,先用锁把变量锁?。?然后再操作 , 操作完了之后再释放掉锁;当另一个线程也要访问这个变量时,发现这个变量被锁住了,无法访问 , 它就会一直等待,直到锁没了,它再给这个变量上个锁 , 然后使用,使用完了释放锁,以此进行 。这样即使有多个线程同时访问这个变量 , 也好像是对这个变量的操作是顺序进行的 。
互斥变量使用特定的数据类型:pthread_mutex_t 。使用互斥量前要先初始化,初始化又分为静态初始化和动态初始化:
  • 静态初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
  • 动态初始化:pthread_mutex_init(&mutex,NULL);
第一种方法仅局限于静态初始化的时候使用:将「声明、定义、初始化」一气呵成,除此之外的情况都只能使用 pthread_mutex_init函数 。
2.2 pthread_mutex_init函数原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
头  文  件:#include <pthread.h>
返  回  值:成功返回 0,失败返回错误码
参数介绍:
  1. mutex:指向 pthread_mutex_t 声明的变量的地址
  2. attr:指定了新建互斥锁的属性 。一般置为 NULL(如果参数attr为 NULL,则使用默认的互斥锁属性,默认属性为快速互斥锁 ) 。
restrict 关键字只用于限制指针 。告诉编译器所有修改该指针指向内存中的操作,只能通过本指针完成,不能通过除了本指针之外的变量或指针修改 。

推荐阅读