Unix C语言 POSIX 互斥锁 线程同步

た 入场券 2022-10-22 07:39 274阅读 0赞

文章目录

      • 模拟并发冲突
        • 出现该问题的原因
      • POSIX的互斥锁

由于线程的并发性,导致多个线程同时对同一个全局变量进行操作时,会出现数据不一致的情况。
所以需要在线程中对全局变量的操作上互斥锁,来确保该操作同一时刻只能被一个线程进行,从而确保了数据安全。

模拟并发冲突

先不着急看互斥锁,先看下互斥锁可以解决什么问题。
下面的代码有个int型的num全局变量,开启多个线程同时对该num进行+1的动作,每个线程+1执行1000000次,所有线程执行完毕后,num的值应该等于线程数*1000000。但由于并发执行,结果是小于线程数*1000000的。
可以自行跑代码。
注意编译需要链接pthread库:gcc ... -lpthread

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. int num = 0; //全局变量
  5. /** * 函数执行体。类似java中的Thread#run函数。 * 当函数体执行完毕,则线程结束。 * @param arg 创建线程时传入的参数。 * @return 线程返回的结果,可以被创建该线程的父线程通过pthread_join获取。 */
  6. void *thread_proc(void *arg) {
  7. for (int i = 0; i < 1000000; ++i) {
  8. num++;
  9. }
  10. return NULL;
  11. }
  12. int main(void) {
  13. int tcount = 3; //线程数
  14. pthread_t tids[tcount];
  15. for (long long i = 1; i <= tcount; ++i) { //创建多个线程
  16. int errno = pthread_create(&tids[i - 1], NULL, thread_proc, (void *) i); //创建线程
  17. if (errno) { //当线程创建失败,返回的errno非0
  18. fprintf(stderr, "pthread_create:%s\n", strerror(errno)); //打印异常信息
  19. return -1;
  20. }
  21. }
  22. for (int i = 0; i < tcount; ++i) {
  23. pthread_t tid = tids[i];
  24. pthread_join(tid, NULL);
  25. printf("线程%lld结束。\n", (long long) tid);
  26. }
  27. printf("所有线程执行完毕,num=%d\n", num);
  28. getchar();//防止主线程退出,回车即退出。
  29. return 0;
  30. }

出现该问题的原因

代码中的num++虽说是一行代码,但实际对于CPU来说是3个指令操作:将num从内存加载到CPU寄存器、寄存器num+1、寄存器num+1后的值写入内存。
由于多个线程同时并发,并错落执行对num的操作指令,CPU来回切换的线程上下文中的num与内存中的num会不一致,进而导致最终的num并非是按照循环次数的正确数字。

POSIX的互斥锁

要解决上面的问题,那就要对线程中存在并发操作共享数据的代码进行上锁,使其操作线程安全。

互斥锁的API和线程相关的API一样,都在<pthread.h>这个头文件中。
创建互斥锁有两种方式,一种是静态创建,另一种是动态创建:
-静态创建
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
-动态创建
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); :动态创建互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex); :释放动态创建的互斥锁资源

-上锁解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);:上互斥锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);:解互斥锁

基于前面的问题代码进行上锁操作,会发现最终的num等于线程数*1000000

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. int num = 0; //全局变量
  5. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态创建互斥锁
  6. /** * 函数执行体。类似java中的Thread#run函数。 * 当函数体执行完毕,则线程结束。 * @param arg 创建线程时传入的参数。 * @return 线程返回的结果,可以被创建该线程的父线程通过pthread_join获取。 */
  7. void *thread_proc(void *arg) {
  8. for (int i = 0; i < 1000000; ++i) {
  9. pthread_mutex_lock(&mutex); //上锁
  10. num++;
  11. pthread_mutex_unlock(&mutex); //解锁
  12. }
  13. return NULL;
  14. }
  15. int main(void) {
  16. int tcount = 3;
  17. pthread_t tids[tcount];
  18. for (long long i = 1; i <= tcount; ++i) { //创建多个线程
  19. int errno = pthread_create(&tids[i - 1], NULL, thread_proc, (void *) i); //创建线程
  20. if (errno) { //当线程创建失败,返回的errno非0
  21. fprintf(stderr, "pthread_create:%s\n", strerror(errno)); //打印异常信息
  22. return -1;
  23. }
  24. }
  25. for (int i = 0; i < tcount; ++i) {
  26. pthread_t tid = tids[i];
  27. pthread_join(tid, NULL);
  28. printf("线程%lld结束。\n", (long long) tid);
  29. }
  30. printf("所有线程执行完毕,num=%d\n", num);
  31. getchar();//防止主线程退出,回车即退出。
  32. return 0;
  33. }

发表评论

表情:
评论列表 (有 0 条评论,274人围观)

还没有评论,来说两句吧...

相关阅读

    相关 POSIX 互斥&属性块

    目录 互斥锁属性块 1.互斥锁属性块的初始化和删除 2.设置和获取互斥锁属性块的类型 3.设置和获取互斥锁属性块的算法类型 4.设置和获取互斥锁属性块的天花板优先级

    相关 linux线同步互斥

    互斥锁(互斥量)是线程用来同步彼此行为的工具。互斥锁可以帮助线程同步对共享资源的使用,以防如下情况发生:线程某甲试图访问一共享变量时,线程某乙正在对其修改。 未避免线程更新共