linux互斥锁(pthread_mutex)知识点总结

news/2024/12/2 20:31:49/

对于多线程程序来说,我们往往需要对这些多线程进行同步。同步(synchronization)是指在一定的时间内只允许某一个线程访问某个资源。而在此时间内,不允许其它的线程访问该资源。我们可以通过互斥锁(mutex),条件变量(condition variable)和读写锁(reader-writer lock)来同步资源。

与互斥锁相关API

  互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去等待它重新变为可用。在这种方式下,每次只有一个线程可以向前运行。

  在设计时需要规定所有的线程必须遵守相同的数据访问规则。只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其它的线程在使用共享资源前都获取了锁,也还是会出现数据不一致的问题。

  互斥变量用pthread_mutex_t数据类型表示。在使用互斥变量前必须对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化。如果动态地分配互斥量(例如通过调用malloc函数),那么在释放内存前需要调用pthread_mutex_destroy。

1. 创建及销毁互斥锁

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
// 返回:若成功返回0,否则返回错误编号

  要用默认的属性初始化互斥量,只需把attr设置为NULL。  

  2. 加锁及解锁

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 返回:若成功返回0,否则返回错误编号

  如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁。如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock就会失败,不能锁住互斥量,而返回EBUSY。

示例代码:

#include <stdio.h>
#include <pthread.h>//int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data = 0;pthread_mutex_t mutex;void *func1(void *arg)
{int i;pthread_mutex_lock(&mutex);for(i=0;i<5;i++){printf("t1:%ld thread is create\n",(unsigned long)pthread_self());printf("t1:param is %d\n",*((int *)arg));sleep(1);}pthread_mutex_unlock(&mutex);}void *func2(void *arg)
{pthread_mutex_lock(&mutex);printf("t2:%ld thread is create\n",(unsigned long)pthread_self());printf("t2:param is %d\n",*((int *)arg));pthread_mutex_unlock(&mutex);}void *func3(void *arg)
{pthread_mutex_lock(&mutex);printf("t3:%ld thread is create\n",(unsigned long)pthread_self());printf("t3:param is %d\n",*((int *)arg));pthread_mutex_unlock(&mutex);}int main()
{int ret;int param = 100;pthread_t t1;pthread_t t2;pthread_t t3;pthread_mutex_init(&mutex, NULL);ret = pthread_create(&t1, NULL, func1,(void *)&param);if(ret == 0){printf("main:create t1 success\n");}ret = pthread_create(&t2, NULL, func2,(void *)&param);if(ret == 0){printf("main:create t2 success\n");}ret = pthread_create(&t3, NULL, func3,(void *)&param);printf("main:%ld\n",(unsigned long)pthread_self());pthread_join(t1,NULL);pthread_join(t2,NULL);pthread_mutex_destroy(&mutex);return 0;
}


编译结果:t1会被main函数打断,但是不会被t2,t3打断,t1运行后t2,t3才能运行

我们也用互斥锁限制共享资源的访问

例如:让g_data = 3时,在线程t1退出

#include <stdio.h>
#include <pthread.h>//int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data = 0;pthread_mutex_t mutex;void *func1(void *arg)
{printf("t1:%ld thread is create\n",(unsigned long)pthread_self());printf("t1:param is %d\n",*((int *)arg));pthread_mutex_lock(&mutex);while(1){printf("t1: %d\n",g_data++);    sleep(1);if(g_data == 3){pthread_mutex_unlock(&mutex);printf("t1 quit================================\n");
//            pthread_exit(NULL);exit(0);}}}void *func2(void *arg)
{printf("t2:%ld thread is create\n",(unsigned long)pthread_self());printf("t2:param is %d\n",*((int *)arg));while(1){printf("t2: %d\n",g_data);pthread_mutex_lock(&mutex);g_data++;pthread_mutex_unlock(&mutex);    sleep(1);}
}int main()
{int ret;int param = 100;pthread_t t1;pthread_t t2;pthread_mutex_init(&mutex,NULL);ret = pthread_create(&t1, NULL, func1,(void *)&param);if(ret == 0){printf("main:create t1 success\n");}ret = pthread_create(&t2, NULL, func2,(void *)&param);if(ret == 0){printf("main:create t2 success\n");}printf("main:%ld\n",(unsigned long)pthread_self());while(1){printf("main: %d\n",g_data);    sleep(1);}pthread_join(t1,NULL);pthread_join(t2,NULL);pthread_mutex_destroy(&mutex);return 0;
}


编译结果:

可以写个shell脚本验证一下编译结果,先简单介绍一下shell脚本吧

Shell脚本

Shell 脚本(shell script),是一种为 shell 编写的脚本程序。shell script是一种解释型语言,必须由解释器来执行这些脚本,执行时,解释器将脚本一行一行地转换为代码。

这个解释器就是Shell,它是一个用 C 语言编写的程序。常见的Shell有Bourne Shell(/usr/bin/sh或/bin/sh)和Bourne Again Shell(/bin/bash),sh由Steve Bourne开发,是Unix 标准默认的shell,bash由Brian Fox和Chet Ramey共同开发完成,是Linux标准默认的shell。


在linux中可以使用vim来编写shell script,通常使用.sh作为扩展名,sh代表shell。下面是一个简单的shell脚本:

//运行三次

编写完shell脚本,在执行脚本前我们需要给脚本添加执行权限,命令如下:

接着运行:

编译结果:


http://www.ppmy.cn/news/100343.html

相关文章

二分搜索树层序遍历

二分搜索树的层序遍历&#xff0c;即逐层进行遍历&#xff0c;即将每层的节点存在队列当中&#xff0c;然后进行出队&#xff08;取出节点&#xff09;和入队&#xff08;存入下一层的节点&#xff09;的操作&#xff0c;以此达到遍历的目的。 通过引入一个队列来支撑层序遍历…

c++—文件编程

1. c对文件的操作是由文件流类完成的&#xff0c;文件流类在流类与文件间建立连接&#xff1b; 2. 文件流类型 ①文件输入流&#xff1a;ifstream ②文件输出流&#xff1a;ofstream ③文件输入/输出流&#xff1a;fstream 3. 语法 ①定义文件流类的对象 ifstream ifile; …

张小飞的Java之路——第四十一章——File

写在前面&#xff1a; 视频是什么东西&#xff0c;有看文档精彩吗&#xff1f; 视频是什么东西&#xff0c;有看文档速度快吗&#xff1f; 视频是什么东西&#xff0c;有看文档效率高吗&#xff1f; 介绍 诸小亮&#xff1a;从今天开始&#xff0c;我们学习 IO 流 张小飞…

三门问题的实验验证:贝叶斯概率公式实战

引言 数理统计与概率论经常出现在我们的日常生活中&#xff0c;如果能灵活掌握&#xff0c;可以起到很大的帮助。下面通过几个经典问题的探讨&#xff0c;浅入深出&#xff0c;更加深刻的理解贝叶斯全概率公式和贝叶斯公式的作用。 我的最深的体会就是&#xff0c;当某些已发生…

c++—封装:构造函数、析构函数、成员操作

1. 封装的主要目的是解决代码的维护性问题&#xff0c;经过封装的函数代码独立性高&#xff1b; 2. 封装的演变历史&#xff0c;以栈为例子介绍&#xff1a; ①成员&#xff08;top、data[ ]&#xff09;都在main函数里&#xff0c;动作方法&#xff08;push、pop&#xff09;…

应急响应-windows

win系统常见的安全事件 1.病毒&#xff0c;木马&#xff0c;蠕虫事件 2.web服务器入侵事件或第三方服务入侵事件 3.系统入侵事件&#xff0c;用win漏洞入侵系统&#xff0c;利用弱口令等。 4.网络攻击事件&#xff0c;如DDos&#xff0c;ARP欺骗等。 win系统安全事件发现的…

【经验总结】浮点数double/float精度误差问题总结

现象 最近做的项目中经常会在C环境下和高精度的double浮点类型数据打交道 这些double类型数据精度级别可能到 pico级别(10^-12) 甚至 femto级别(10^-15),用来表示集成电路的一些微观属性 但是非常诡异的是&#xff0c;不知道为什么在对这些高精度的浮点数进行运算时&#xff…

Threejs进阶之十六:音频可视化

最近事情比较多&#xff0c;博客更新的有点慢了&#xff0c;今天更新一期&#xff0c;主要聊一聊通过Threejs提供的音频API实现音频的可视化效果&#xff0c;先看下最终实现的效果 音频可视化 目录 Threejs中音频相关的类Audio 类构造函数常用属性常用方法创建Audio对象示例 Au…