多线程编程杂谈(上)

问题

线程执行的过程中可以强制退出吗?

主动退出?被动退出?

问题抽象示例

需要解决的问题

g_run 全局变量需要保护吗?

如何编码使得线程中每行代码的执行可被 g_run 控制?

线程代码在被 g_run 控制并 "强制退出" 时:

  • 如何指定线程的返回值?
  • 如何回收初始化时申请的资源?

关键:如何实现 g_run 的原子化操作

以下是原子操作吗?如果不是,有什么影响?

  • g_run = 1
  • if(g_run) {}
  • while(g_run) {}
  • g_run++, g_run--, g_run += n, ...

下面的程序会输出什么?为什么?

该程序创建100个线程,每个线程会对全局变量 g_var 进行 10000次加一操作,但由于 g_var 是全局变量,是临界资源,并且 g_var++ 操作不是原子操作,g_var++ 在汇编层面分为三个步骤,首先是将 g_var的值读到某个寄存器上,然后是在寄存器上对这个值加一,最后将加一后的值写入到 g_var,所以这个程序的运行结果的打印,g_var 的值会小于等于 1000000

现代 C 语言的原子化特性

_Atomic 是一个关键字,被修饰的变量具有原子化特性

_Atomic 是与 const 和 volatile 同类型的关键字 (可搭配 typedef 使用)

思考

_Atomic 是现代 C 语言中的关键字,如果不使用它,int 类型变量的操作还是原子的吗?

变量原子性测试

test1.c

#define _GNU_SOURCE     /* To get pthread_getattr_np() declaration */
#define _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <memory.h>
#include <semaphore.h>static _Atomic volatile int g_var = 0;void* thread_func(void* arg)
{  int i = 0;for(i=0; i<10000; i++){g_var++;}return NULL;
}int main()
{pthread_t t[100] = {0};int i = 0;void* ret = NULL;for(i=0; i<100; i++){pthread_create(&t[i], NULL, thread_func, NULL);}for(i=0; i<100; i++){pthread_join(t[i], &ret);}printf("g_var = %d\n", g_var);return 0;
}

第 11 行,我们将 g_var 通过 _Atomic 关键字将它修饰为原子变量,对 g_var 的操作不可被打断

程序运行结果如下图所示:

由于g_var 通过 _Atomic 关键字将它修饰为原子变量,所以 g_var++ 变成了原子操作

从微观进行分析

将 C 代码转换为汇编代码可以看出,g_var = 1 和 if(g_var) 是原子操作,而 g_var++ 不是原子操作

结论

对于 int 型全局变量,赋值操作是原子操作 (g_var = 1)

基于值的条件判断是原子操作 (if(g_var))

递增 / 递减 操作不是原子操作 (++, --, +=, -=)

线程退出解决方案

({}) 是 gcc 中的语法,这种语法允许在一个表达式的上下文中包含多条语句,这样可以使得宏定义 R 具有返回值,返回值为 code

R 这个宏定义会判断 g_run,如果 g_run 为 0,则调用 pthread_exit() 来结束线程的执行,线程结束的返回值默认为 NULL,也可填在 R 中的第二个参数指定;非 0 则执行 code

注意事项

不是每一行代码都需要使用 R(...) 宏进行线程退出控制

建议编码时,精心设计线程退出的 关键点 及 返回值

实践中,通常以代码块为粒度进行线程退出控制

临界区代码一定不要 R(...) 宏进行线程退出控制 (可能造成死锁)

线程退出解决方案

test2.c

#define _GNU_SOURCE     /* To get pthread_getattr_np() declaration */
#define _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <memory.h>
#include <semaphore.h>static volatile int g_run = 0;
static _Atomic volatile int g_var = 0;#define R(code, ...)    ({                   \if( !g_run )                             \pthread_exit((NULL, ##__VA_ARGS__)); \\code;                                    \
})void cleanup_handler(void* arg)
{printf("%s : %p\n", __FUNCTION__, arg);free(arg);
}void* thread_func(void* arg)
{  int i = 0;char* pc = malloc(16);if( pc ){pthread_cleanup_push(cleanup_handler, pc);R(strcpy(pc, "Hello World!"), (void*)111);R(printf("%s\n", pc));for(i=0; i<10000; i++){R(g_var++);}pthread_cleanup_pop(1);}return NULL;
}int main()
{pthread_t t[100] = {0};int i = 0;void* ret = NULL;for(i=0; i<100; i++){pthread_create(&t[i], NULL, thread_func, NULL);}for(i=0; i<100; i++){pthread_join(t[i], &ret);if( ret ){printf("ret = %lld\n", (long long)ret);}}printf("g_var = %d\n", g_var);return 0;
}

第 34 行,我们通过 pthread_cleanup_push(...) 函数来注册线程退出时的资源清理函数,当 thread_func 线程退出时,会调用 cleanup_handler(...) 函数来清理资源,这里是用来释放 pc 所指向的堆空间

在线程可能会中途退出的代码片段中使用宏定义 R(...),但不宜每个地方都使用,这样会影响程序的执行效率

程序运行结果如下图所示:

由于 g_run 的值为 0,所以每个子线程执行到第 36 行,就会结束,并调用到资源清理函数 cleanup_handler,拿到的 pthread_exit(...) 的返回值也是 111

思考

是否存在其它中途退出线程的方法?

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

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

相关文章

【Git】:企业级开发和多人协作开发啊

目录 多人协作 模拟配置多人协作环境 多人同一分支开发 多人不同分支开发 远程分支删除后的问题 企业级开发模型 系统开发环境 分支设计规范 多人协作 模拟配置多人协作环境 目前&#xff0c;我们所完成的工作如下&#xff1a; 基本完成 Git 的所有本地库的相关操作&#xff…

【Sql优化】数据库优化方法、Explain使用

文章目录 一、金字塔优化模型二、SQL优化的利器&#xff1a;Explain工具1. Explain 的作用2. Explain 的用法 三、SQL优化方法&#xff08;后续文章细讲&#xff09;1. 创建索引减少扫描量2. 调整索引减少计算量3. 索引覆盖4. 干预执行计划5. SQL改写 四、通过 Explain 优化案例…

Deepmotion技术浅析(五):运动追踪

运动追踪是 DeepMotion 动作捕捉和 3D 重建流程中的核心模块之一。该模块的主要任务是在视频序列中跟踪人体的运动轨迹&#xff0c;捕捉人体各部分随时间的变化&#xff0c;并生成连续的 3D 运动数据。DeepMotion 的运动追踪技术结合了计算机视觉、深度学习和物理模拟等方法&am…

Android 系统应用重名install安装失败分析解决

Android 系统应用重名install安装失败分析解决 文章目录 Android 系统应用重名install安装失败分析解决一、前言1、Android Persistent apps 简单介绍 二、系统 persistent 应用直接安装需求分析解决1、系统应用安装报错返回的信息2、分析解决 三、其他1、persistent系统应用in…

使用Nexus3搭建npm私有仓库

一、npm介绍 npm的全称是Node Package Manager&#xff0c;它是一个开放源代码的命令行工具&#xff0c;用于安装、更新和管理Node.js模块。npm是Node.js的官方模块管理器&#xff0c;它允许用户从一个集中的仓库中下载和安装公共的Node.js模块&#xff0c;并将这些模块集成到…

【ChatGPT】解锁AI思维链:如何让机器像人类一样思考?

在人工智能领域&#xff0c;我们一直在追求让机器像人类一样思考。然而&#xff0c;即使是最先进的AI&#xff0c;也常常被诟病缺乏“常识”&#xff0c;难以理解复杂问题&#xff0c;更不用说像人类一样进行逻辑推理和解决问题了。最经常的表现就是遇到不会的地方&#xff0c;…

蓝桥杯刷题——day5

蓝桥杯刷题——day5 题目一题干解题思路一代码解题思路二代码 题目二题干解题思路代码 题目一 题干 给定n个整数 a1,a2,⋯ ,an&#xff0c;求它们两两相乘再相加的和&#xff0c;即&#xff1a; 示例一&#xff1a; 输入&#xff1a; 4 1 3 6 9 输出&#xff1a; 117 题目链…

监测预警智能分析中心建设项目方案

随着科技的不断进步&#xff0c;地理信息与遥感技术在国家治理、环境保护、灾害预警等领域发挥着越来越重要的作用。监测预警智能分析中心的建设&#xff0c;旨在通过集成先进的遥感技术、地理信息系统&#xff08;GIS&#xff09;、大数据分析和人工智能&#xff08;AI&#x…

【漫话机器学习系列】009.词袋模型(Bag of Words)

词袋模型&#xff08;Bag of Words, 简称 BoW&#xff09; 词袋模型是一种常见的文本表示方法&#xff0c;主要用于自然语言处理&#xff08;NLP&#xff09;和信息检索领域。它将文本数据转换为特征向量&#xff0c;忽略语序&#xff0c;仅考虑词的出现与否或出现频率。 1. 基…

vue3 setup语法,子组件点击一个元素打印了这个元素的下标id,怎么传递给父组件,让父组件去使用

问&#xff1a; vue3 setup语法&#xff0c;子组件点击一个元素打印了这个元素的下标id&#xff0c;怎么传递给父组件&#xff0c;让父组件去使用 回答&#xff1a; 在 Vue 3 中&#xff0c;你可以使用 setup 语法糖和组合式 API 来实现子组件向父组件传递数据。具体来说&am…

分治算法(单选题)

2-1 分数 2 下列多少种排序算法用了分治法&#xff1f; 堆排序插入排序归并排序快速排序选择排序希尔排序 A.2 B.3 C.4 D.5 正确答案 A 2-2 分数 2 分治法的设计思想是将一个难以直接解决的大问题分割成规模较小的子问题&#xff0c;分别解决问题&#xff0c;最后将子…

【栈】栈的定义及基本操作

1. 栈的定义和特点 定义&#xff1a;栈是限定尽在表尾进行插入或删除操作的线性表。 表头元素成为栈底&#xff0c;表尾元素成为栈顶。 特点&#xff1a;后进先出&#xff08;先进后出&#xff09; 2. 顺序栈 顺序栈是利用顺序存储结构实现的栈&#xff0c;即用一组连续…

UNIX简史

从1991年Linux出现至今&#xff0c;由于众多IT巨头以及技术社区的推动&#xff0c;Linux已经成为非常成熟、可用于各种关键领域的操作系统&#xff0c;适当了解其发展历史&#xff0c;对于理顺其技术流派、从而更好地学习和使用Linux具有重要意义。由于其基于UNIX系统二十多年的…

C# OpenCV机器视觉:畸变矫正

在一个阳光明媚的早晨&#xff0c;阿强决定去拍照。他拿起相机&#xff0c;穿上他最喜欢的羊毛大衣&#xff0c;准备记录下生活中的美好瞬间。可是&#xff0c;当他兴奋地查看照片时&#xff0c;发现自己拍的每一张都像是被外星人用变形金刚的力量扭曲过一样&#xff01;“这是…

读书|关于马斯克

于我而言&#xff0c;马斯克是一个有魅力的人&#xff0c;他张扬嚣张、却又一直做着惊世骇俗的事情。 关于健康 马斯克本身的工作就十分忙碌&#xff0c;但早年的他生活习惯也极其不规律&#xff0c;睡眠不足、饮食糊弄、懒得运动。健康三要素一个不占。另外&#xff0c; 42 …

tryhackme——Defensive Security Intro(防御安全简介)

任务一&#xff1a;Introduction to Defensive Security防御安全简介 此room的两个要点&#xff1a; Preventing intrusions from occurring 防止入侵发生Detecting intrusions when they occur and responding properly 检测发生的入侵并正确响应 防御安全还有更多内容。 除上…

使用rust语言创建python模块(pyo3+maturin)

1. 首先使用conda创建python虚拟环境&#xff08;已创建的可省略&#xff09; >conda create --prefixE:\python_envs\rust_python python3.11 2. 激活python虚拟环境 conda activate rust_python 3. 安装maturin pip install maturin 4. 创建rust项目 >cd E:\py…

阿里云RAM实战详解

引言 阿里云RAM(Resource Access Management)是一款用于管理阿里云资源访问权限的服务。通过RAM,您可以为不同的用户分配不同的访问权限,确保资源的安全和可控。本文将详细介绍RAM的实战应用,结合最佳实践,帮助您更好地管理阿里云资源。 RAM的核心概念 在使用RAM之前,…

解锁CSS新维度:预处理器之LessSass

在现代前端开发中&#xff0c;CSS&#xff08;层叠样式表&#xff09;是用于控制网页外观的主要技术。然而&#xff0c;随着项目的复杂度增加&#xff0c;传统的CSS编写方式逐渐显现出其局限性&#xff0c;如变量复用、嵌套规则、模块化管理等需求难以满足。为此&#xff0c;出…

C++中函数的特性

文章目录 一、C中形参带默认值的函数二、C中的inline内联函数三、C中的inline内联函数和普通函数的区别四、C中函数重载五、C为什么支持函数重载六、C语言为什么无法实现函数重载 一、C中形参带默认值的函数 在C中&#xff0c;形参带默认值的函数是指在函数声明或定义时&#…