Linux系统编程之常用线程同步的三种方法

2023-11-06

Linux系统编程之线程同步高效率编程

         ~~~~~~~~         Linux系统中线程最大的特点就是共享性,线程同步问题较为困难也很重要,最常用的三种是:条件变量、互斥锁、无名信号量。(ps: 有名信号量可用于进程同步,无名信号量只能用于线程同步,是轻量级的。)


(一)、【互斥锁】:mutex

线程互斥量数据类型:pthread_mutex_t

  1. 初始化锁
    静态分配: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
             ~~~~~~~~          参数一:创建的互斥锁
             ~~~~~~~~          参数二:存储互斥锁信息的结构,一般为NULL

  2. 加锁:对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
    int pthread_mutex_lock(pthread_mutex *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex);
             ~~~~~~~~         参数:指明互斥锁

  3. 解锁:在完成了对共享资源的访问后,要对互斥量进行解锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
             ~~~~~~~~         参数:指明互斥锁

  4. 销毁锁
    int pthread_mutex_destroy(pthread_mutex *mutex);
             ~~~~~~~~         参数:指明互斥锁


(二)、【条件变量】:cond

         ~~~~~~~~         条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
数据类型:pthread_cond_t

  1. 初始化
    静态:pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    动态:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
             ~~~~~~~~         参数一:指明条件变量
             ~~~~~~~~         参数二:存储条件变量属性的结构>
             ~~~~~~~~         
  2. 等待条件成立:释放锁,同时等待条件为真才能有停止阻塞。
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
             ~~~~~~~~         参数一:指明条件变量
             ~~~~~~~~         参数二:指明互斥锁
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
             ~~~~~~~~         
  3. 激活条件变量:
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
      ~  
  4. 注销条件变量
    int pthread_cond_destroy(pthread_cond_t *cond);

(三)、【无名信号量】:sem

注意:链接需要加上-pthread选项
例如:gcc -pthread main.c -o main
  ~  
         ~~~~~~~~         有名信号量可用于进程的同步,头文件:#include<sys/sem.h>,;而无名信号量只能用于线程,是轻量级,头文件:#include <semaphore.h>)

  1. 初始化:
    int sem_init (sem_t *sem , int pshared, unsigned int value);
             ~~~~~~~~         参数一:指明信号量
             ~~~~~~~~         参数二:共享选项(linux 只支持为0,即表示它是当前进程的局部信号量)
             ~~~~~~~~         参数三:设置初始值
             ~~~~~~~~         
  2. 等待信号量:给信号量减1,然后等待直到信号量的值大于0。
    int sem_wait(sem_t *sem);
      ~  
  3. 释放信号量:
    int sem_post(sem_t *sem);
      ~  
  4. 销毁信号量:
    int sem_destroy(sem_t *sem);

【DEMO】

         ~~~~~~~~         现有两个同学打扫卫生,学生A负责扫地,学生B负责拖地,很明显要扫完地之后在拖地,由此引入线程同步。

1、条件变量和互斥锁的联合利用实现
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)

void *student_1();
void *student_2();


int num = 0;	//共享资源
pthread_mutex_t mulock = PTHREAD_MUTEX_INITIALIZER;		//互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;			//条件变量
pthread_t stu_thread[2];		//两个学生线程
int main()
{
	int i;
	
	// 创建两个学生线程
	pthread_create(&stu_thread[0], NULL, student_1, NULL);
	pthread_create(&stu_thread[1], NULL, student_2, NULL);
	
	// 等待两个线程结束
	for(i=0; i<2; i++)
	{
		pthread_join(stu_thread[i], NULL);
	}
	
	// 注销操作
	pthread_mutex_destroy(&mulock);
	pthread_cond_destroy(&cond);
	return 0;
}

void *student_1()
{
	int i;
	DEBUG_INFO("student a start work .\n");
	
	for(i=0; i<5; i++)
	{
		DEBUG_INFO("i = %d \n", i);
		pthread_mutex_lock(&mulock);	//锁住
		num++;		//扫一次地
		if(num >= 5)
		{
			DEBUG_INFO("student a finished work .\n");
			pthread_cond_signal(&cond);	//通知学生B已经扫完地了,并解锁
		}
		
		pthread_mutex_unlock(&mulock);
		sleep(1);
	}
	
	pthread_exit(NULL);
	return 0;
}
void *student_2()
{
	DEBUG_INFO("in student 2 .. \n");
	while(num < 5)		//不用if四因为需要防止莫名错误
	{
		pthread_cond_wait(&cond, &mulock);	//等待学生A扫地结束,等不到会再次一直阻塞
	}
	
	num = 0;	//拖地
	pthread_mutex_unlock(&mulock);
	DEBUG_INFO("student b finished work .\n");
	pthread_exit(NULL);
	return 0;
}


Makefile
这里写图片描述
运行结果
这里写图片描述
         ~~~~~~~~         由运行结果可见,学生A先完成工作,学生B在完成工作,所以成功实现线程同步。

2、信号量实现
/****************************************************************************************
* 文件名: demo2.c
* 创建者: 
* 时 间: 
* 联 系: 
* 简 介: 
*****************************************************************************************/

#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)

int num = 0;	//共享资源
sem_t mysem; //用于同步的信号量
pthread_t stu_thread[2];		//两个学生线程

void *student_1();
void *student_2();


int main()
{
	// 初始化信号量
	sem_init(&mysem, 0, 0);
	
	int i;
	
	// 创建两个学生线程
	pthread_create(&stu_thread[0], NULL, student_1, NULL);
	pthread_create(&stu_thread[1], NULL, student_2, NULL);
	
	// 等待两个线程结束
	for(i=0; i<2; i++)
	{
		pthread_join(stu_thread[i], NULL);
	}
	
	// 注销操作
	sem_destroy(&mysem);
	return 0;
}


void *student_1()
{
	int i;
	DEBUG_INFO("student a start work .\n");
	
	for(i=0; i<5; i++)
	{
		DEBUG_INFO("i = %d \n", i);
		num++;		//扫一次地
		if(num >= 5)
		{
			DEBUG_INFO("student a finished work .\n");
			sem_post(&mysem);	//释放信号量
		}
		
		sleep(1);
	}
	
	pthread_exit(NULL);
	return 0;
}
void *student_2()
{
	DEBUG_INFO("in student 2 .. \n");
	sem_wait(&mysem);		//等待信号量	
	num = 0;	//拖地
	DEBUG_INFO("student b finished work .\n");
	pthread_exit(NULL);
	return 0;
}


【运行结果】
这里写图片描述
         ~~~~~~~~         由运行结果可见,用信号量的程序运行结果与使用条件变量结果一致,所以实验成功!


end…

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux系统编程之常用线程同步的三种方法 的相关文章

  • Locale.getDefault() 始终返回 en

    unix 机器上的服务器始终使用 en 作为默认区域设置 以下是区域设置输出 LANG en US LC CTYPE C LC NUMERIC C LC TIME C LC COLLATE C LC MONETARY C LC MESSAG
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • 使用 find - 删除除任何一个之外的所有文件/目录(在 Linux 中)

    如果我们想删除我们使用的所有文件和目录 rm rf 但是 如果我希望一次性删除除一个特定文件之外的所有文件和目录怎么办 有什么命令可以做到这一点吗 rm rf 可以轻松地一次性删除 甚至可以删除我最喜欢的文件 目录 提前致谢 find ht
  • 为什么我收到“无法进行二进制日志记录”的信息。在我的 MySQL 服务器上?

    当我今天启动 MySQL 服务器并尝试使用以下命令进行一些更改时用于 MySQL 的 Toad http www quest com toad for mysql 我收到此消息 MySQL 数据库错误 无法进行二进制日志记录 消息 交易级别
  • 抑制 makefile 中命令调用的回显?

    我为一个作业编写了一个程序 该程序应该将其输出打印到标准输出 分配规范需要创建一个 Makefile 当调用它时make run gt outputFile应该运行该程序并将输出写入一个文件 该文件的 SHA1 指纹与规范中给出的指纹相同
  • 如何使用 bash 锁定文件

    我有一个任务从远程服务器同步目录 rsync av email protected cdn cgi l email protection srv data srv data 为了使其定期运行并避免脚本 reEnter 问题 我使用 rsyn
  • 仅打印“docker-container ls -la”输出中的“Names”列

    发出时docker container ls la命令 输出如下所示 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a67f0c2b1769 busybox tail f dev
  • Linux中的CONFIG_OF是什么?

    我看到它在很多地方被广泛使用 但不明白在什么场景下我需要使用它 What is 配置 OF OF 的全名是什么 打开固件 这是很久以前发明的 当时苹果公司正在生产基于 PowerPC CPU 的笔记本电脑 而 Sun Microsystem
  • 大多数 Linux 系统头文件与 C++ 兼容吗?

    大多数 Linux 系统头文件 API C 兼容吗 今天我试图做这样的事情 include
  • 我可以从命令行打印 html 文件(带有图像、css)吗?

    我想从脚本中打印带有图像的样式化 html 页面 谁能建议一个开源解决方案 我使用的是 Linux Ubuntu 8 04 但也对其他操作系统的解决方案感兴趣 你可以给html2ps http user it uu se jan html2
  • sendfile64 只复制约2GB

    我需要使用 sendfile64 复制大约 16GB 的文件 到目前为止我所取得的成就是 include
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • nslookup 报告“无法解析 '(null)': 名称无法解析”,尽管它成功解析了 DNS 名称

    我在 ubuntu 上 并且正在运行 docker 默认桥接网络 我有 Zookeeper kafka 的容器化版本 以及我编写的与 kafka 对话的应用程序 I do a docker exec it
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 如何在Linux内核源代码中打印IP地址或MAC地址

    我必须通过修改 Linux 内核源代码来稍微改变 TCP 拥塞控制算法 但为了检查结果是否正确 我需要记录 MAC 或 IP 地址信息 我使用 PRINTK 函数来打印内核消息 但我感觉很难打印出主机的MAC IP地址 printk pM
  • Linux 内核标识符中前导和尾随下划线的含义是什么?

    我不断遇到一些小约定 比如 KERNEL Are the 在这种情况下 是内核开发人员使用的命名约定 还是以这种方式命名宏的语法特定原因 整个代码中有很多这样的例子 例如 某些函数和变量以 甚至 这有什么具体原因吗 它似乎被广泛使用 我只需
  • os.Mkdir 和 os.MkdirAll 权限

    我正在尝试在程序开始时创建一个日志文件 我需要检查是否 log如果不创建目录 则目录存在 然后继续创建日志文件 好吧 我尝试使用os Mkdir 也os MkdirAll 但无论我在第二个参数中输入什么值 我都会得到一个没有权限的锁定文件夹
  • Linux:在文件保存时触发 Shell 命令

    我想在修改文件时自动触发 shell 命令 我认为这可以通过注册 inotify 挂钩并调用来在代码中完成system 但是是否有更高级别的 bash 命令可以完成此任务 尝试 inotify 工具 我在复制链接时遇到问题 抱歉 但 Git
  • 在 Linux 上使用多处理时,TKinter 窗口不会出现

    我想生成另一个进程来异步显示错误消息 同时应用程序的其余部分继续 我正在使用multiprocessingPython 2 6 中的模块来创建进程 我试图用以下命令显示窗口TKinter 这段代码在Windows上运行良好 但在Linux上
  • 使用 sh 运行 bash 脚本

    我有 bash 脚本 它需要 bash 另一个人尝试运行它 sh script name sh 它失败了 因为 sh 是他的发行版中 dash 的符号链接 ls la bin sh lrwxrwxrwx 1 root root 4 Aug

随机推荐

  • 在线应用的 Serverless 实践

    作者 唐慧芬 黛忻 阿里云产品专家 导读 毫无疑问 Serverless 能够在效率和成本上给用户带来巨大收益 那具体到落地又应该怎么做呢 本文就给大家详细解读 Serverless 的落地实践 Serverless 落地企业级应用的挑战
  • XML外部实体漏洞(XXE)详解与编程

    XML外部实体漏洞 XXE 详解与编程 XML外部实体漏洞 XXE 是一种常见的网络安全漏洞 它允许攻击者通过操纵XML解析器来访问和读取服务器上的文件 甚至执行远程代码 本文将详细介绍XXE漏洞的原理和常见的攻击方式 并提供相应的源代码示
  • idea实现类快捷生成接口方法

    接口类 实现类 当我们实现了接口后 并没有像eclipse那样 鼠标放上去就会提示生成接口方法 鼠标定位到UserService类后面 快捷键 Alt Enter 选择Implement methods 选中点OK就自动生成了 还有一种方法
  • 《C++ Primer》读书笔记第十六章-1-定义模板

    笔记会持续更新 有错误的地方欢迎指正 谢谢 这一章特别实用 神器 gt 模板 泛型编程能处理在编译之前类型不知道的情况 在编译时获知类型 比如我们学过的容器 迭代器和算法都是泛型编程 模板是C 中泛型编程的基础 记住 一个模板就是一个创建类
  • netCDF文件的scale_factor和add_offset

    我使用python的netCDF4读取数据 发现数据集存在scale factor和add offset 但是我读取的数据应该是Unpacking data 也就是转换后的实际数据 不需要再处理 因为数据压缩是通过偏移和缩放之后将浮点数转化
  • 【C++】const修饰的成员函数

    在日常学习中总是碰到const修饰的成员函数 自己也总是稀里糊涂的 只能是靠着编译器来区分const和非const成员函数的相互调用关系 今天在这里总结以下 一 const修饰成员函数的格式 在成员函数的后边加上const void dis
  • NUC980开源项目11-启动方式

    上面是我的微信和QQ群 欢迎新朋友的加入 项目码云地址 国内下载速度快 https gitee com jun626 nuc980 open source project 项目github地址 https github com Jun117
  • Windows7/10上快速搭建Tesseract-OCR开发环境操作步骤

    之前在https blog csdn net fengbingchun article details 51628957 中描述过如何在Windows上搭建Tesseract OCR开发环境 那时除了需要clone https github
  • MySQL——事务和视图

    2023 9 17 本章开始介绍TCL语言 Transaction Control Language 事务控制语言 事务 事务的概念 一个或一组sql语句组成一个执行单元 这个执行单元要么全部执行 要么全部不执行 事务的特性 ACID 原子
  • scala---spark本地调式远程获取hdfs数据注意事项

    文章目录 前言 一 Hadoop配置注意事项 1 1 core site xml 1 2 core site xml 二 本地hadoop环境配置注意事项 三 本地scala项目spark代码调试 总结 前言 这篇文章主要帮大家绕开一些本地
  • 异常关机后Oracle无法正常连接,使用 conn /as sysdba 出现 ORA-01034 和 ORA-27101: shared memory realm does not exist...

    最近异常关机导致oracle无法连接 一直提示ORA 01034和ORA 27101的错误 打开cmd后 输入 sqlplus npolog conn as sysdba 提示 ORA 01034 Oracle not available
  • windows10使用WSL安装Linux(以ubuntu为例)

    1 安装工具WSL 适用于 Linux 的 Windows 子系统 WSL 可让开发人员直接在 Windows 上按原样运行 GNU Linux 环境 包括大多数命令行工具 实用工具和应用程序 且不会产生传统虚拟机或双启动设置开销 是win
  • 浙大水业oa系统服务器地址,OA系统

    OA系统功能定位于知识管理 企业决策支持 资源共享和企业协同工作 它由单纯的办公自动化向提升到协助管理整个企业为目标 表现在以下四个方面 把协同工作融入业务流程中 团队中通过及时的交流 准确的任务分派从而实现高绩效管理 E OFFICE办公
  • 通过js修改网页内容

    js可以通过文本所在标签的id获取该标签对象 然后修改其内容 如 document getElementById 标签id innerHTML 要修改的文本内容 该方法可以在要修改的文本内容中加html标签 如果只是纯文本的话 可以使用in
  • 严重性 代码 说明 项目 文件 行 禁止显示状态

    严重性 代码 说明 项目 文件 行 禁止显示状态 错误 LNK2019 无法解析的外部符号 public void thiscall LinkedList
  • 解决ubuntu无法输入中文标点

    使用Ctrl 切换
  • ListBox控件 滚动条

    今天在使用LISTBOX控件中遇到的一点小问题 主要是两个问题 水平滚动条不显示内容 垂直滚动条没有自动滚动 在网上查了一下找到了解决办法 原来只需要向控件发送消息就行了 具体代码如下 以下都是在Dialog类中的函数操作 如果是使用 Se
  • C++编程规范(101条规则、准则与最佳实践)

    C 编程规范 101条规则 准则与最佳实践 虽然是书本的目录 但也是高度的概括和总结 组织和策略问题 第0条 不要拘泥于小节 了解哪些东西不应该标准化 第1 条 在高警告级别干净利落地进行编译 第2 条 使用自动构建系统 第3 条 使用版本
  • 解决uniapp在微信小程序显示图片/数据,h5不显示图片/数据。

    配置跨域 首先在mainifest json中的源码视图中配置跨域 h5 devServer port 8080 disableHostCheck true proxy dpc target https www edonguoji cn c
  • Linux系统编程之常用线程同步的三种方法

    Linux系统编程之线程同步高效率编程 Linux系统中线程最大的特点就是共享性 线程同步问题较为困难也很重要 最常用的三种是 条件变量 互斥锁 无名信号量 ps 有名信号量可用于进程同步 无名信号量只能用于线程同步 是轻量级的 一 互斥锁