Linux 多线程同步机制(上)

news2025/1/23 14:53:18

文章目录

  • 前言
  • 一、线程同步
  • 二、互斥量 mutex
  • 三、死锁
  • 总结

前言


一、线程同步

在多线程环境下,多个线程可以并发地执行,访问共享资源(如内存变量、文件、网络连接  等)。
这可能导致 数据不一致性, 死锁, 竞争条件等 问题。
为了解决这些问题,需要使用同步机制来确保线程间的协作和互斥访问共享资源。

“同步” 的目的 是为了避免数据的混乱,解决与时间有关的错误。实际上,不仅线程需要同步,进程间,信号间等等都需要同步机制。

线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时 其他线程为保证数据的一致性,不能调用该功能。

二、互斥量 mutex

互斥锁(Mutex,全称为 Mutual Exclusion)是一种常用的同步机制,用于保护共享资源免受多个线程同时访问和修改的影响。互斥锁提供了一种互斥访问的机制,同一时间只允许一个线程获取锁并访问被保护的资源。

每个线程在对资源操作前都尝试进行先加锁,成功加锁才能操作,操作结束解锁。
资源还是共享的,线程也还是竞争的。
但 通过 “锁” 就将资源的访问变成互斥操作,而后与时间有关的错误也就不会再产生了。

1. 互斥锁的基本操作包括两个关键操作:

  • 加锁(Lock):线程通过申请互斥锁来获取对共享资源的访问权。如果互斥锁当前未被其他线程获取,线程成功获得锁然后进入临界区(Critical Section),可以访问共享资源。如果互斥锁已经被其他线程获取,申请锁的线程将被阻塞,直到锁被释放。

  • 解锁(Unlock):线程在完成对共享资源的访问之后,释放互斥锁,使得其他线程可以申请并获取锁。

2. 互斥锁的主要应用函数 :

pthread_mutex_init: 用于初始化互斥锁变量。
pthread_mutex_destroy: 用于销毁互斥锁对象。
pthread_mutex_lock: 用于加锁,如果互斥锁已被其他线程占用,则当前线程阻塞。
pthread_mutex_trylock: 尝试加锁,如果互斥锁已被其他线程占用,则返回一个失败状态而不阻塞线程。
pthread_mutex_unlock: 用于解锁,释放互斥锁使其他线程可以获取。

3. 初始化线程锁 :
有两种方式可以对互斥锁进行初始化:静态初始化和动态初始化。

  • 静态初始化: 是在定义互斥锁变量时直接进行初始化,不需要调用特定的初始化函数。
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    PTHREAD_MUTEX_INITIALIZER 是一个宏,用于静态初始化互斥锁变量。
  • 动态初始化:动态初始化是在运行时使用初始化函数对互斥锁进行初始化。
    pthread_mutex_init(&mutex, NULL);

4. 示例代码
在下面代码中,main 函数中有一个主线程 打印小写字母,my_thread 为 子线程 打印 大写字母。两个线程通过互斥锁来访问 共享资源。

#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

pthread_mutex_t lock;							// 创建 互斥锁

void *my_thread(void *arg)
{
	srand(time(NULL));							// 设置随机种子
	
	while(1)
	{
		pthread_mutex_lock(&lock);
		printf("ABC ");
		sleep(rand() % 3);
		printf("XYZ\n");

		pthread_mutex_unlock(&lock);
		sleep(rand() % 3);							// 休眠随机秒,释放cpu资源
	}
	
	pthread_exit(NULL);
}


int main(void)
{
	pthread_t tid;
	int ret;

	srand(time(NULL));									// 设置随机种子

	ret = pthread_mutex_init(&lock,  NULL);				// 初始化互斥锁
	if(ret != 0)
	{
		printf("pthread_mutex_init err\n");
	}

	ret = pthread_create(&tid, NULL, my_thread, NULL);
	if(ret != 0)
	{
		printf("pthread_create err\n");
	}

	while(1)
	{
		pthread_mutex_lock(&lock);
		printf("abc ");
		sleep(rand() % 3);
		printf("xyz\n");

		pthread_mutex_unlock(&lock);
		sleep(rand() % 3);
	}

	pthread_mutex_destroy(&lock);			  	// 销毁 互斥锁

	pthread_join(tid,NULL);				  		// 等待回收线程,获取回收状态

	return 0;
}

注意 :
锁粒度(Lock Granularity):锁的粒度应该尽可能小,以避免锁定过长时间,从而降低了并发性能。

三、死锁

死锁产生的原因:死锁是指多个线程或进程因为彼此相互等待对方所持有的资源而无法继续执行的状态。

解决:

  1. 使用资源的有序性:通过规定线程获取资源的顺序,避免出现循环等待的情况。例如,可以约定所有线程按照一定的顺序获取资源,从而避免死锁的发生。

如果下面两个线程 获取资源的顺序是相反的,则可能会产生死锁。可以将 线程 B 先获取 m1锁,再获取 m2锁。
在这里插入图片描述
以下面代码的方式获取锁,不会存在死锁风险。

#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void *my_thread1(void *arg)
{
	pthread_mutex_lock(&lock1);
	printf("my_thread1 : begin\n");

	pthread_mutex_lock(&lock2);
	printf("my_thread1 : end\n");

	pthread_mutex_unlock(&lock2);
	pthread_mutex_unlock(&lock1);
	
	pthread_exit(NULL);
}

void *my_thread2(void *arg)
{
	pthread_mutex_lock(&lock1);
	printf("my_thread2 : begin\n");

	pthread_mutex_lock(&lock2);
	printf("my_thread2 : end\n");

	pthread_mutex_unlock(&lock2);
	pthread_mutex_unlock(&lock1);
	
	pthread_exit(NULL);
}

int main(void)
{
	pthread_t tid1,tid2;
	int ret;

	ret = pthread_create(&tid1, NULL, my_thread1, NULL);
	if(ret != 0)
	{
		printf("pthread1_create err\n");
	}

	ret = pthread_create(&tid2, NULL, my_thread2, NULL);
	if(ret != 0)
	{
		printf("pthread2_create err\n");
	}

	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	return 0;
}
  1. 设置超时机制:在请求资源时,设置一个超时时间,在超过该时间后如果仍未获得资源,则放弃等待,释放已经获取的资源,避免长时间的死锁等待。

总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/942615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode-中等题】138. 复制带随机指针的链表

文章目录 题目解题核心思路&#xff1a;找random指针指向思路一&#xff1a;哈希思路二&#xff1a;迭代构造新链表 方法一&#xff1a;哈希递归方法二&#xff1a;纯哈希方法三&#xff1a;迭代 节点拆分 题目 解题核心思路&#xff1a;找random指针指向 这里的拷贝属于深拷…

Leetcode刷题:395. 至少有 K 个重复字符的最长子串、823. 带因子的二叉树

Leetcode刷题:395. 至少有 K 个重复字符的最长子串、823. 带因子的二叉树 1. 395. 至少有 K 个重复字符的最长子串算法思路参考代码和运行结果 2. 823. 带因子的二叉树算法思路参考代码和运行结果 1. 395. 至少有 K 个重复字符的最长子串 题目难度&#xff1a;中等 标签&#…

lenovo联想笔记本小新Air-15 2021款Intel版ITL版(82GM)原装出厂Win10系统

自带所有驱动、出厂主题壁纸LOGO、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;ISO 文件大小&#xff1a;11.2GB 链接&#xff1a;https://pan.baidu.com/s/12NTMOt5eUjOIsbayXPyiww?pwdrs4v 提取码&#xf…

基于单片机的数字温度计设计

一、项目背景 数字温度计是一种用于测量和显示环境温度的设备。本文章介绍基于STC89C52主控芯片的数字温度计的设计过程和实现原理。该设计采用DS18B20温度传感器进行温度采集&#xff0c;使用LCD1602显示屏进行温度显示&#xff0c;通过按键设置温度的上限和下限阀值&#xf…

点云配准算法之NDT

1 前言 很久之前记录了一篇博客PCL点云配准_thequitesunshine007的博客-CSDN博客 &#xff0c;记录的是基于点特征&#xff08;FPFH特征描述子&#xff09;匹配的SAC-ICP点云配准思想。 今天记录一下完全不一样的点云配准方法NDT。 2 介绍 2.1 多元正态分布 如果随机变量X满…

Servlet与Web容器的初探

Servlet 是用Java编写的服务端程序&#xff0c;具有独立于平台和协议的特性&#xff0c;主要功能在于交互式地浏览和生成数据&#xff0c;生成动态Web内容。 Servlet也需要帮助。请求到来时&#xff0c;必须有人实例化Servlet&#xff0c;或者至少要建立一个新的线程处理这个请…

js 正则表达式 验证 :页面中一个输入框,可输入1个或多个vid/pid,使用英文逗号隔开...

就是意思一个输入框里面&#xff0c;按VID/PID格式输入,VID和PID最大长度是4,最多50组 1、页面代码 <el-form ref"ruleForm" :model"tempSet" :rules"rules" label-position"right"> <!-- 最多 50组&#xff0c;每组9个字符…

RT_Thread内核机制学习(三)进程间通信

队列 队列里有多个消息块&#xff0c;每个消息块大小一致。 写&#xff1a; 有空间&#xff0c;成功。无空间&#xff1a;返回Err&#xff1b;等待一段时间。 队列里面会有两个链表&#xff1a;发送链表和接收链表 struct rt_messagequeue {struct rt_ipc_object parent; …

ChatGPT Prompting开发实战(二)

一、基于LangChain源码react来解析prompt engineering 在LangChain源码中一个特别重要的部分就是react&#xff0c;它的基本概念是&#xff0c;LLM在推理时会产生很多中间步骤而不是直接产生结果&#xff0c;这些中间步骤可以被用来与外界进行交互&#xff0c;然后产生new con…

C#,数值计算——双指数DE (double exponential)结构的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Structure for implementing the DE (double exponential) rule. /// </summary> public class DErule : Quadrature { private double a { get; set; …

基于SpringBoot的员工(人事)管理系统

基于SpringBoot的员工&#xff08;人事&#xff09;管理系统 一、系统介绍二、功能展示三.其他系统实现五.获取源码 一、系统介绍 项目名称&#xff1a;基于SPringBoot的员工管理系统 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 前端技术&#xff1a;BootS…

【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式

【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式 文章目录 【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式一、概述二、什么是幂等性三、幂等性需关注几个重点四、幂等性有什么用五、常见用来保证幂等的手段5.1 MVCC方案5.2 去重表5.3 去重表5.4 select in…

5G 数字乡村数字农业农村大数据中心项目农业大数据建设方案PPT

导读&#xff1a;原文《5G 数字乡村数字农业农村大数据中心项目农业大数据建设方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。以下是部分内容&#xff0c; 喜…

市级数字政府电子政务大数据中心项目建设和运营方案WORD

导读&#xff1a;原文《市级数字政府电子政务大数据中心项目建设和运营方案WORD》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。以下是部分内容&#xff0c; 目 录 …

5、监测数据采集物联网应用开发步骤(5.1)

监测数据采集物联网应用开发步骤(4) Sqlite3数据库读写操作开发、异常信息统一处理类开发 本章节需要调用sqlite3及mysql-connector 安装sqlite3 Pip3 install sqlite3 安装mysql-connector pip3 install mysql-connector 验证是否安装成功&#xff0c;python中运行下列…

Unity网格编程笔记[十]一些网格基础操作的封装(Mesh合并,UV映射,正反面反转,顶点合并,法线求切线计算等)

这里的代码是在 Unity网格编程笔记[五]网格切割 中整合出来的。 这里的mesh可以直接接入到使用mesh的unity组件 一些基础的属性还是要参考 Unity网格编程笔记[零]网格编程基础知识点 Mesh合并 网格的合并&#xff0c;其实底层也没那么复杂。对于三角面&#xff0c;只是顺序…

LiveGBS伴侣

【1】LiveGBS 简介 LiveGBS是一套支持国标(GB28181)流媒体服务软件。 国标无插件;提供用户管理及Web可视化页面管理&#xff1b; 提供设备状态管理&#xff0c;可实时查看设备是否掉线等信息&#xff1b; 实时流媒体处理&#xff0c;PS&#xff08;TS&#xff09;转ES&…

python的安装(推荐)

torch安装与卸载推荐链接1推荐链接2 推荐链接3 安装pytorch步骤推荐链接 python关键字&#xff1a;

19.CSS雨云动画特效

效果 源码 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Cloud & Rain Animation</title><link rel="stylesheet" href="style.css"> </head> <bo…

SpringCluod深入教程

1.Nacos配置管理 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我们需要一种统一配置管理方案&#…