参考文献:

温故知新

在 OS 中,每个进程都独立地拥有:

  • Process ID, process group ID, user ID, and group ID
  • Environment
  • Working directory
  • Program instructions
  • Registers
  • Stack
  • Heap
  • File descriptors
  • Signal actions
  • Shared libraries
  • Inter-process communication tools (such as message queues, pipes, semaphores, or shared memory).

因此,使用 fork 开启一个新的进程,需要拷贝很多数据,开销较大。

与进程不同,线程需要只需要独立拥有:

  • Stack pointer
  • Registers
  • Scheduling properties (such as policy or priority)
  • Set of pending and blocked signals
  • Thread specific data

需要特别注意的是,文件描述符和堆空间是进程独有的,因此该进程下面的所有线程都共用该进程的堆与文件描述符。

例如下面的代码:

  1. #include <stdlib.h>
  2. #include <pthread.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. void *worker1(void *arg)
  6. {
  7. char *p = malloc(25);
  8. memcpy(p, "heap data from worker1", 23);
  9. return p;
  10. };
  11. void *worker2(void *arg)
  12. {
  13. pthread_t tid1 = *(pthread_t *)arg;
  14. char *ptr = NULL;
  15. pthread_join(tid1, (void **)&ptr);
  16. printf("In worker2: ");
  17. if (ptr) puts(ptr);
  18. return NULL;
  19. }
  20. int main()
  21. {
  22. pthread_t tid1, tid2;
  23. pthread_create(&tid1, NULL, worker1, NULL);
  24. pthread_create(&tid2, NULL, worker2, &tid1);
  25. pthread_join(tid2, NULL);
  26. }

线程同步

在描述这个概念之前,先看一段代码:

  1. #include <stdlib.h>
  2. #include <pthread.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. const int N = 1e4;
  6. int value = 0;
  7. void *worker1(void *arg)
  8. {
  9. int i = 1;
  10. for (; i <= N / 2; i++) value = value + i;
  11. return NULL;
  12. };
  13. void *worker2(void *arg)
  14. {
  15. int i = N / 2 + 1;
  16. for (; i <= N; i++) value = value + i;
  17. return NULL;
  18. }
  19. int main()
  20. {
  21. pthread_t tid1, tid2;
  22. pthread_create(&tid1, NULL, worker1, NULL);
  23. pthread_create(&tid2, NULL, worker2, NULL);
  24. pthread_join(tid1, NULL);
  25. pthread_join(tid2, NULL);
  26. printf("%d\n", value);
  27. printf("SUM(1, %d) should be %d .\n", N, N * (N + 1) / 2);
  28. }

显然,我们想通过 2 个线程实现 SUM(1, N) 这个功能,但是编译多次你会发现,value 的值并不准确,有时候能输出正确答案 500500,有时候却不能。

这是因为 work1work2 是并发执行的,假设一开始,2 个线程同时计算 value + iwork1work2 分别得到 15001,但是写入 value 变量是有先后顺序的。假设 work1 先写入,work2 后写入,那么对于这 2 次累加,value 的最终结果是 5001 ,而不是 5002

从这个例子可以看出,线程与进程类似,同样需要同步 (Synchronization) ,对于临界资源,每次只允许一个线程访问。

互斥量 mutex

互斥量,也叫互斥锁。mutex, 即 Mutual exclusion , 意为相互排斥,主要用于实现线程同步中的写保护操作。

pthread_mutex_init

初始化一个互斥量 pthread_mutex_t mutex .

函数原型:

  1. int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

参数:

  • mutex 是即将要被初始化的互斥量
  • attr 是互斥量的属性,与 pthread_attr_t 类似

与之类似的,还有 pthread_mutex_destroy 函数。

使用方法:

  1. pthread_mutex_t mutex;
  2. pthread_mutex_init(&mutex, NULL);
  3. pthread_mutex_destroy(&mutex);

pthread_mutex_lock

阻塞调用。如果这个互斥锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个互斥锁量的等待队列中,并会进入阻塞状态, 直到拿到该锁之后才会返回。

函数原型如下:

  1. int pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_trylock

非阻塞调用。当请求的锁正在被占用的时候, 不会进入阻塞状态,而是立刻返回,并返回一个错误代码 EBUSY,意思是说, 有其它线程正在使用这个锁。

直白点的说法,请求资源,能拿到就拿,拿不到我就继续往下执行。

函数原型:

  1. int pthread_mutex_trylock(pthread_mutex_t *mutex);

pthread_mutex_unlock

释放互斥锁。

函数原型如下:

  1. int pthread_mutex_unlock(pthread_mutex_t *mutex);

对于这些 API,如果成功,那么返回 0,否则返回错误码 errno ,可以通过下列宏定义打印错误信息:

  1. #define handler_error_en(en, msg) \
  2. do \
  3. { \
  4. errno = en; \
  5. perror(msg); \
  6. exit(EXIT_FAILURE); \
  7. } while (0)

例子:同步累加

对于「线程同步」一节给出的例子,使用互斥量实现同步操作,使得程序能够正确完成累加操作。

  1. #include <stdlib.h>
  2. #include <pthread.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. const int N = 1e4;
  6. int value = 0;
  7. pthread_mutex_t mutex;
  8. void *worker1(void *arg)
  9. {
  10. int i = 1;
  11. int sum = 0;
  12. for (; i <= N / 2; i++) sum += i;
  13. // 这样保证 value 仅能由一个线程访问
  14. pthread_mutex_lock(&mutex);
  15. value += sum;
  16. pthread_mutex_unlock(&mutex);
  17. return NULL;
  18. };
  19. void *worker2(void *arg)
  20. {
  21. int i = N / 2 + 1;
  22. int sum = 0;
  23. for (; i <= N; i++) sum += i;
  24. pthread_mutex_lock(&mutex);
  25. value += sum;
  26. pthread_mutex_unlock(&mutex);
  27. return NULL;
  28. }
  29. int main()
  30. {
  31. pthread_t tid1, tid2;
  32. pthread_mutex_init(&mutex, NULL);
  33. pthread_create(&tid1, NULL, worker1, NULL);
  34. pthread_create(&tid2, NULL, worker2, NULL);
  35. pthread_join(tid1, NULL);
  36. pthread_join(tid2, NULL);
  37. printf("%d\n", value);
  38. printf("SUM(1, %d) should be %d .\n", N, N * (N + 1) / 2);
  39. pthread_mutex_destroy(&mutex);
  40. }

pthread 互斥量的更多相关文章

  1. pthread中互斥量,锁和条件变量

    互斥量 #include <pthread.h> pthread_mutex_t mutex=PTHREAD_MUTEX_INTIIALIZER; int pthread_mutex_in ...

  2. php Pthread 多线程 (三) Mutex 互斥量

    当我们用多线程操作同一个资源时,在同一时间内只能有一个线程能够对资源进行操作,这时就需要用到互斥量了.比如我们对同一个文件进行读写操作时. <?php class Add extends Thr ...

  3. Linux/Unix 线程同步技术之互斥量(1)

    众所周知,互斥量(mutex)是同步线程对共享资源访问的技术,用来防止下面这种情况:线程A试图访问某个共享资源时,线程B正在对其进行修改,从而造成资源状态不一致.与之相关的一个术语临界区(critic ...

  4. Linux 多线程互斥量互斥

    同步 同一个进程中的多个线程共享所在进程的内存资源,当多个线程在同一时刻同时访问同一种共享资源时,需要相互协调,以避免出现数据的不一致和覆盖等问题,线程之间的协调和通信的就叫做线程的同步问题, 线程同 ...

  5. linux线程同步(1)-互斥量

    一.概述                                                   互斥量是线程同步的一种机制,用来保护多线程的共享资源.同一时刻,只允许一个线程对临界区进行 ...

  6. [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

    一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...

  7. 共享内存+互斥量实现linux进程间通信 分类: Linux C/C++ 2015-03-26 17:14 67人阅读 评论(0) 收藏

    一.共享内存简介 共享内存是进程间通信中高效方便的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针,两个进程可以对一块共享 ...

  8. [linux basic 基础]----同步互斥量

    互斥量,运行程序元锁住某个对象,使得每次只能有一个线程访问它:为了控制对关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后在完成操作之后解锁它 :基本函数与用于信号量的函数非常相似#inclu ...

  9. 【Linux】Mutex互斥量线程同步的例子

    0.互斥量  Windows下的互斥量 是个内核对象,每次WaitForSingleObject和ReleaseMutex时都会检查当前线程ID和占有互斥量的线程ID是否一致. 当多次Wait**时就 ...

随机推荐

  1. CF1295E Permutation Separation

    线段树 难得把E想出来,写出来,但却没有调出来(再给我5分钟),我的紫名啊,我一场上紫的大好机会啊 首先考虑是否能将$k$在$1$--$n-1$的每一个的最小代价都求出来 因为$k$从$i$到$i-1 ...

  2. Spring Security 实战干货:OAuth2第三方授权初体验

    1. 前言 Spring Security实战干货系列 现在很多项目都有第三方登录或者第三方授权的需求,而最成熟的方案就是OAuth2.0授权协议.Spring Security也整合了OAuth2. ...

  3. Ros中创建msg和srv遇到的问题

    在创建msg和srv文件之后,使用srv和msg文件时候需要对xml文件进行修改,如下: <build_depend>message_generation</build_depend ...

  4. Docker - 使用 Jenkins 镜像创建容器,并搭建 Python + Pytest +Allure 的自动化测试环境

    如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 安装 Docker 直接参考我这 ...

  5. 云计算之路-出海记:命令行下的 AWS

    俗话说"三百六十行,行行出状元",自从有了电脑之后,三百六十行又多了一行 -- 命令行.GUI 的诞生开创了繁荣的 PC "窗口"(windows)时代,互联网 ...

  6. 2.while循环

    while循环 #-*- coding: utf-8-*- #指定识别utf-8的字符串 1.while循环以及跳出循环 while True: #无限循环 print('i love pyhon') ...

  7. Jmeter-全局变量跨线程组使用

    一.前言 前面讲了如何使用正则表达式提取值,一般提取的值在同一个线程里,随意哪个请求都是可以引用的,那如果别的线程组也想引用怎么办呢?这时就涉及到一个全局变量的知识点了,话不多说,直接实例走起. 二. ...

  8. kudu1.10基于cdh6.3.1搭建

    1.下载kudu依赖: yum -y install cyrus-sasl-plain ntp   2.下载kudu rpm包: wget https://archive.cloudera.com/c ...

  9. JS超酷时钟的制作

    通过补充代码,实现时钟实时显示当前时间:年.月.日.时.分.秒.日期. <!DOCTYPE html> <html> <head lang="zh-CN&quo ...

  10. 全文思维导图------redis设计与实现