参考文献:

温故知新

在 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. Docker(5)- docker version 命令详解

    如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 作用 显示 Docker 版本信 ...

  2. 【Kata Daily 190917】Numericals of a String(字符出现的次数)

    题目: You are given an input string. For each symbol in the string if it's the first character occuren ...

  3. inno steup 安装判断 进程是否运行

    1.添加了卸载判断用语 2.添加了安装程序进程是否存在使用了wmi服务 ; 脚本由 Inno Setup 脚本向导 生成! ; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档! #d ...

  4. 经典c程序100例==41--50

    [程序41] 题目:学习static定义静态变量的用法 1.程序分析: 2.程序源代码: #include "stdio.h" varfunc() { int var=0; sta ...

  5. Python的ConfigParser模块读取ini配置文件 报错(持续更新总结)

    1.ConfigParser.MissingSection什么的错误巴拉巴拉一堆,其实根本上就是没有读到配置文件,然后我去检查了一遍路径,发现没有问题,我是将文件的路径作为一个字符串拼接好传到另一个专 ...

  6. 【转】CentOS7 64位安装mysql教程

    从最新版本的linux系统开始,默认的是 Mariadb而不是mysql!这里依旧以mysql为例进行展示 1.先检查系统是否装有mysql rpm -qa | grep mysql 这里返回空值,说 ...

  7. Python_PyQt5_eric6 做省市县筛选框

    eric是PyQt5的图形化编辑工具,界面如下(另存为-桌面  查看大图) 下面是用eric6制作的 省市县 三级联动筛选框 (效果图+源码) 1 # -*- coding: utf-8 -*- 2 ...

  8. redis源码学习之slowlog

    目录 背景 环境说明 redis执行命令流程 记录slowlog源码分析 制造一条slowlog slowlog分析 1.slowlog如何开启 2.slowlog数量限制 3.slowlog中的耗时 ...

  9. Linux文件监控工具——inotify-tools

    举例: ip.txt内容如下: 10.1.1.11 root 123 10.1.1.22 root 111 10.1.1.33 root 123456 10.1.1.44 root 54321 写法1 ...

  10. Redis的一些攻击手法整理

    Redis基础 1 Redis基础 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开 ...