Linux系统编程(六)线程同步机制

本文目录

  • 前述:同步机制的引入及概念
  • 一、线程的互斥锁
    • 1. 定义
    • 2. 互斥锁常用方法
    • 3. 相关函数
      • (1)头文件
      • (2)创建互斥锁
      • (3)销毁互斥锁
      • (4)加锁
      • (5)解锁
    • 4. 使用例程

  

前述:同步机制的引入及概念

为了更全面的学习,在学习本章之前,请查看《线程的创建与使用》这篇文章。
   进程中的多线程之间使用的是共享的数据,那么如果多个线程同时操作同一个共享资源,那么岂不是很混乱了。为了解决这个问题,引入了线程间的同步机制,即每次对共享资源进行操作时,只能有一个线程操作,其他线程必须等待其操作完毕后再进行操作。
   线程的同步机制是指在多线程编程中,用来协调线程之间的操作,以确保它们按预期顺序执行,避免竞态条件、死锁和其他并发问题。同步机制的主要目的是管理对共享资源的访问,确保数据一致性和程序的正确性。

序号机制描述
1互斥锁互斥锁的主要作用是同步,确保同一时刻只有一个线程能够进入临界区,从而避免竞态条件。例如,多个线程需要访问或修改同一个共享变量时,可以用互斥锁保护这个变量。
2条件变量条件变量用于线程等待某个条件成立,通常与互斥锁结合使用。
3信号量/灯信号量是一个计数器,用于控制对资源的访问,允许多个线程同时访问一定数量的资源。

一、线程的互斥锁

1. 定义

   在 Posix Thread 中定义了一套专门用于线程互斥的 mutex 函数。mutex 是一种简单的加锁的方法来控制对共享资源的存取。这个互斥锁只有两种状态(上锁和解锁)。

问题:为什么需要对共享资源加锁?
   答:因为多个线程共用进程的资源,要访问的是公共区间时(全局变量),当一个线程访问的时候,需要加上锁以防止另外的线程对它进行访问,以实现资源的独占。在一个时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程才能够对共享资源进行操作。若其他线程希望上锁一个已经上锁了的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。

   注意:要使用互斥锁时,一定要避免死锁的出现!死锁是指两个或多个线程相互等待对方释放资源,从而导致所有线程都无法继续执行。避免死锁是多线程编程中的一个重要问题。且不应该在信号处理函数中使用互斥锁。

2. 互斥锁常用方法

    互斥锁类型: ( 1)快速锁(普通锁),(2)嵌套锁(递归锁),(3)检错锁。 这里我们使用最多的就是快速锁,即普通锁。

   互斥锁有两种创建(初始化)方法:静态方式(使用宏)和动态方式(用函数创建)。我们常使用动态创建。

3. 相关函数

(1)头文件

#include <pthread.h>

(2)创建互斥锁

   互斥锁需要在线程创建之前初始化好,以确保在任何线程尝试使用锁之前,锁已经准备就绪。这样做是为了防止竞争条件和潜在的未定义行为。

int pthread_mutex_init(pthread_mutex_t *mutex,  const pthread_mutexattr_t *mutexattr)
//pthread_mutex_t *mutex :传入互斥锁句柄的地址。
//const pthread_mutexattr_t *mutexattr :填NULL,表示该锁为普通锁。

(3)销毁互斥锁

   销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。

int pthread_mutex_destroy(pthread_mutex_t *mutex);
//pthread_mutex_t *mutex :传入互斥锁的句柄地址。

(4)加锁

   对共享资源进行操作前,需要进行加锁操作。

int pthread_mutex_lock(pthread_mutex_t *mutex)
//pthread_mutex_t *mutex :传入互斥锁的句柄地址。

(5)解锁

   对共享资源操作完毕后,需要进行解锁操作,以便其他线程使用这个共享资源。

int pthread_mutex_unlock(pthread_mutex_t *mutex)
//pthread_mutex_t *mutex :传入互斥锁的句柄地址。

4. 使用例程

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h> 

pthread_mutex_t mutex;       //互斥锁句柄
int Sum_tick= 10000000;      //假如:总票数。(公共资源)利用互斥锁保护。

void *task(void *arg)
{    
	long index= (long)arg;    //用index接收arg的值,区别第几个线程。
	int *slef_tick =(int *)malloc(sizeof(int *));         
	*slef_tick =0;   //本窗口售出票数统计
	//避免死锁的出现,必须要有解锁动作
	while(1)
	{
		//如果该线程上锁,则其他线程需要等待解锁才能继续执行。
		pthread_mutex_lock(&mutex);         //上锁:对公共资源使用前
		if(Sum_tick > 0)                        //还有票 
		{   
			 Sum_tick--;
			 pthread_mutex_unlock(&mutex);  //解锁:对公共资源操作完后
			 (*slef_tick)++;
		} 
		else 
		{
			pthread_mutex_unlock(&mutex);    //解锁: 防止死锁出现,当tick < 0时也需要解锁。
			break;                           //下班
		}
	}
	printf("%ld号窗口今日售出%d张票。\n",index, *slef_tick);
	//退出线程并返回slef的数值
	pthread_exit((void *)slef_tick);
}


int main(int argc, char **argv)
{
	int ret;
	pthread_t thread[10];
	long i;
	int *thread_return;   
	int sum=0;             //sum记录所有子线程返回值的和。
	
	//在线程创建之前创建好互斥锁。  NULL为普通锁
	ret=pthread_mutex_init(&mutex, NULL);
	if(ret <0)
	{
		perror("pthread_mutex_init error!\n");
		return -1;
	}
	
	//创建10个线程:
	for(i=0;i<10;i++)
	{
		ret=pthread_create(&thread[i], NULL, task, (void *)i);  //把i的值传给函数func1的形参
		if(ret!=0)
		{
			perror("pthread_create error!\n");
			return -1;
		}
	}
	
	//等待线程释放,并销毁线程资源
	for(i=0;i<10;i++)
	{
		pthread_join(thread[i], (void **)&thread_return);
		sum = sum+ (*thread_return);
	}
	
	printf("今日景点总售出%d张票。\n",sum);
	//销毁互斥锁
	pthread_mutex_destroy(&mutex);
	return 0;
}

为什么每个线程售出的票数都不一样呢?
   答:这是因为线程间也存在竞争,谁抢到CPU的使用权谁就开始执行。我们可以查看CPU的核心数来确定每次可以同时有多少个线程同时运行。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java Sort 方法的使用(包含Arrays.sort(),Collections.sort()以及Comparable,Comparator的使用 )

目录 Comparable && Comparator的使用&#xff1a; Comparable: Comparator: Arrays.sort()的使用: 升序排序&#xff1a; 降序排序&#xff1a; 自定义排序方法&#xff1a; 在日常的刷题或开发中&#xff0c;很多时候我们需要对数据进行排序&#xff0c;以达到我…

【C++修行之道】类和对象(二)类的6个默认成员函数、构造函数、析构函数

目录 一、类的6个默认成员函数 二、构造函数 2.1 概念 2.2 特性 2.2.5 自动生成默认构造函数 不进行显示定义的隐患&#xff1a; 2.2.6 自动生成的构造函数意义何在&#xff1f; 两个栈实现一个队列 2.2.7 无参的构造函数和全缺省的构造函数都称为默认构造函数&#x…

ELK 使用 metricbeat监控数据

IP功能版本192.168.140.153elk-18.13.4192.168.140.153metricbeat8.13.4192.168.140.156elk-28.13.4192.168.140.156metricbeat8.13.4192.168.140.159logstash8.13.4192.168.140.159kibana8.13.4 一、安装ELK 参考文档&#xff1a; https://download.csdn.net/download/weix…

【免费Web系列】JavaWeb实战项目案例四

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 多表操作&员工列表查询 1. 多表关系 关于单表的操作(单表的设计、单表的增删改查)我们就已经学习完了。接下来我们就要来学习多表的操作&#xff0c;首先来学习多表的设计。 项目开发中&#xff0…

2023年全球DDoS攻击现状与趋势分析

天翼安全科技有限公司副总工程师、运营保障部总经理陈林表示&#xff0c;2023年扫段攻击频次快速增长&#xff0c;成为网络基础设施面临的最大威胁。为躲避防御&#xff0c;低速扫段攻击成为主流达到攻击总数的73.19%&#xff1b;43.26%的C段攻击持续时间小于5分钟&#xff0c;…

面试题vue+uniapp(个人理解-面试口头答述)未编辑完整....

1.vue2和vue3的区别&#xff08;vue3与vue2的区别&#xff08;你不知道细节全在这&#xff09;_vue2和vue3区别-CSDN博客&#xff09;参考 Vue3 在组合式&#xff08;Composition &#xff09;API&#xff0c;中使用生命周期钩子时需要先引入&#xff0c;而 Vue2 在选项API&am…

车载以太网的未来:OPEN Alliance下17个技术委员会的最新进展与行业影响(下)

从上篇介绍来看&#xff0c;TC1-TC8大多数处于暂停或完成状态。而TC9-TC17在2023年都有不同程度的进展&#xff0c;让我们继续探索藏在其中的车载以太网的发展和挑战。 TC9 Automotive Ethernet Channel & Components&#xff08;in progress&#xff09; TC9的目标是为通…

基础9 探索图形化编程的奥秘:从物联网到工业自动化

办公室内&#xff0c;明媚的阳光透过窗户洒落&#xff0c;为每张办公桌披上了一层金色的光辉。同事们各自忙碌着&#xff0c;键盘敲击声、文件翻页声和低声讨论交织在一起&#xff0c;营造出一种忙碌而有序的氛围。空气中氤氲着淡淡的咖啡香气和纸张的清新味道&#xff0c;令人…

【PHP项目实战训练】——laravel框架的实战项目中可以做模板的增删查改功能(2)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

linux centos nfs挂载两台服务器挂载统一磁盘目录权限问题

查看用户id id 用户名另一台为 修改uid和gid为相同id&#xff0c;添加附加组 usermod -u500 -Gwheel epms groupmod -g500 epms

SQL Server定期收缩日志文件详细步骤——基于SQL Server 2012

SQL Server定期收缩日志文件详细步骤 一、环境配置1、查看数据库的属性2、文件设置3、备份模式4、查看收缩配置5、查看收缩选项 二、编写作业计划1、选择新建作业2、常规配置3、步骤4、输入内容5、脚本详解6、新建计划7、输入名称、选择执行时间8、查看测试9、查看测试结果 一、…

Mistral发布首个代码生成人工智能模型Codestral 但不可用于商业活动

由微软支持、估值高达 60 亿美元的法国人工智能初创公司 Mistral发布了首个用于编码的生成式人工智能模型&#xff0c;名为 Codestral。Codestral 与其他代码生成模型一样&#xff0c;旨在帮助开发人员编写代码并与之交互。 Mistral 在一篇博文中解释说&#xff0c;它接受过 80…

想学习中医的看过来

近年来&#xff0c;随着短视频的兴起&#xff0c;中国传统文化&#xff0c;在各大平台成为热搜&#xff0c;且有愈演愈烈之势。证明&#xff0c;国人开始重视起自己的文化&#xff0c;这是一个好的现象。中医学&#xff0c;作为传统文化中不可或缺的一员&#xff0c;也受到了广…

分布式架构设计之Base理论深度剖析:从理论到实践的完美融合

文章目录 引言一、Base理论概述1.1. 基本可用&#xff08;Basically Available&#xff09;1.2. 软状态&#xff08;Soft State&#xff09;1.3. 最终一致性&#xff08;Eventually Consistent&#xff09; 二、Base理论在分布式架构设计中的应用2.1. 分布式数据库设计2.2. 分布…

C语言分支和循环(2)

我的相关博客&#xff1a; C语言的分支与循环&#xff08;1&#xff09; 1.switch语句 除了 if 语句外&#xff0c;C语⾔还提供了 switch 语句来实现分⽀结构。 switch 语句是⼀种特殊形式的 的 if...else 结构&#xff0c;⽤于判断条件有多个结果的情况。它把多重 else if…

用Unityhub安装unity2018.3.0和vuforia

打开下载网址 https://unity.cn/releases/full/2018 选择2018.3.x 找到2018.3.0后&#xff0c;点击从UnityHub下载 然后unityhub会弹出安装界面 只勾选这两个&#xff0c;其余的全部取消勾选&#xff0c;默认勾选上的也取消掉&#xff0c;然后点击安装

【观察】研华科技:奏响数智化转型“交响乐”,将新质生产力融入千行百业...

加快发展新质生产力&#xff0c;已成为当前的主旋律。特别是近年来&#xff0c;以人工智能、大模型、大数据、云计算为代表的数字技术的一系列革命性突破&#xff0c;引发了传统生产要素以及以数据为代表的新生产要素的融合与创新配置&#xff0c;不仅成为推动新质生产力发展的…

开发语言Java+前端框架Vue+后端框架SpringBoot开发的ADR药物不良反应监测系统源码 系统有哪些优势?

开发语言Java前端框架Vue后端框架SpringBoot开发的ADR药物不良反应监测系统源码 系统有哪些优势&#xff1f; ADR药物不良反应监测系统具有多个显著的优势&#xff0c;这些优势主要体现在以下几个方面&#xff1a; 一、提高监测效率与准确性&#xff1a; 通过自动化的数据收集…

Flask初体验

这里有一份展示Flask与Python的协同代码&#xff0c;Flask的web页面展示了系统的一个暴露的公共tcp port连接的所有用户ip:port列表。 做完才发现没有什么用处&#xff0c;我的本意是做一个reverse的ssh或者telnet终端。看点有几个&#xff1a; 我原本是打算用multiprocessin…

音视频开发13 FFmpeg 音频 相关格式分析 -- AAC ADTS格式分析

这一节&#xff0c;我们学习常用的音频的格式 AAC&#xff0c;重点是掌握 AAC的传输格式 ADTS 头部的信息&#xff0c;目的是 &#xff1a; 当音频数据有问题的时候&#xff0c;如果是AAC的编码&#xff0c;在分析 头部信息的时候能够根据头部信息 判断问题是否出现在 头部。 A…