参考文献:

温故知新

在 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

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

例如下面的代码:

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
void *worker1(void *arg)
{
char *p = malloc(25);
memcpy(p, "heap data from worker1", 23);
return p;
};
void *worker2(void *arg)
{
pthread_t tid1 = *(pthread_t *)arg;
char *ptr = NULL;
pthread_join(tid1, (void **)&ptr);
printf("In worker2: ");
if (ptr) puts(ptr);
return NULL;
}
int main()
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, worker1, NULL);
pthread_create(&tid2, NULL, worker2, &tid1);
pthread_join(tid2, NULL);
}

线程同步

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

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
const int N = 1e4;
int value = 0;
void *worker1(void *arg)
{
int i = 1;
for (; i <= N / 2; i++) value = value + i;
return NULL;
};
void *worker2(void *arg)
{
int i = N / 2 + 1;
for (; i <= N; i++) value = value + i;
return NULL;
}
int main()
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, worker1, NULL);
pthread_create(&tid2, NULL, worker2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
printf("%d\n", value);
printf("SUM(1, %d) should be %d .\n", N, N * (N + 1) / 2);
}

显然,我们想通过 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 .

函数原型:

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

参数:

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

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

使用方法:

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_destroy(&mutex);

pthread_mutex_lock

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

函数原型如下:

int pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_trylock

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

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

函数原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

pthread_mutex_unlock

释放互斥锁。

函数原型如下:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

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

#define handler_error_en(en, msg) \
do \
{ \
errno = en; \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)

例子:同步累加

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

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
const int N = 1e4;
int value = 0;
pthread_mutex_t mutex;
void *worker1(void *arg)
{
int i = 1;
int sum = 0;
for (; i <= N / 2; i++) sum += i;
// 这样保证 value 仅能由一个线程访问
pthread_mutex_lock(&mutex);
value += sum;
pthread_mutex_unlock(&mutex);
return NULL;
};
void *worker2(void *arg)
{
int i = N / 2 + 1;
int sum = 0;
for (; i <= N; i++) sum += i;
pthread_mutex_lock(&mutex);
value += sum;
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
pthread_t tid1, tid2;
pthread_mutex_init(&mutex, NULL);
pthread_create(&tid1, NULL, worker1, NULL);
pthread_create(&tid2, NULL, worker2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
printf("%d\n", value);
printf("SUM(1, %d) should be %d .\n", N, N * (N + 1) / 2);
pthread_mutex_destroy(&mutex);
}

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. leetcode103:permutations-ii

    题目描述 给出一组可能包含重复项的数字,返回该组数字的所有排列 例如: [1,1,2]的排列如下: [1,1,2],[1,2,1], [2,1,1]. Given a collection of nu ...

  2. Frontpage 三种回车键

    Frontpage的回车键有三种:enter.shift+enter.ctrl+enter. enter键对应于HTML中的<p></p>,是一个段落的分界符. shift+e ...

  3. Core WebApi项目快速入门(二):Filter详解

    Core的核心是DI,面向AOP的编程方式.在.NetCore中AOP的实现很大程度上依赖于Filter.下面就Core WebApi中的Filter进行举例. 其中鉴权中心我个人认为是比较重要的概念 ...

  4. VBA_50段代码总结

    ''                                                          30个有用的VBA代码 '目录: '1--合理使用数组:'2--一次保存并关闭所 ...

  5. post和get、PostMapping、GetMapping和RequestMapping

    PostMapping.GetMapping和RequestMapping PostMapping和GetMapping封装了method="",限制了method,更加规范化. ...

  6. high Performance

    目的 找出系统性能瓶颈(包括硬件瓶颈和软件瓶颈): 提供性能优化的方案(升级硬件?改进系统系统结构?): 达到合理的硬件和软件配置: 使系统资源使用达到最大的平衡. CPU过渡使用会造成大量进程等待C ...

  7. sock skbuf 结构:

    /** * struct sock - network layer representation of sockets * @__sk_common: shared layout with inet_ ...

  8. tcp 输入 prequeue以及backlog队列

    /*ipv4_specific是TCP传输层到网络层数据发送以及TCP建立过程的真正OPS, 在tcp_prot->init中被赋值给inet_connection_sock->icsk_ ...

  9. 创建一个自定义名称的Ceph集群

    前言 这里有个条件,系统环境是Centos 7 ,Ceph 的版本为Jewel版本,因为这个组合下是由systemctl来进行服务控制的,所以需要做稍微的改动即可实现 准备工作 部署mon的时候需要修 ...

  10. Spring @Autowired 注解自动注入流程是怎么样?

    面试中碰到面试官问:"Spring 注解是如果工作的?",当前我一惊,完了这不触及到我的知识误区了吗?,还好我机智,灵机一动回了句:Spring 注解的工作流程倒还没有看到,但是我 ...