0

0

c++如何实现一个可重入函数_c++线程安全与函数设计原则

冰火之心

冰火之心

发布时间:2025-11-16 16:17:03

|

309人浏览过

|

来源于php中文网

原创

可重入函数指可在执行中被中断并重新进入而不影响结果的函数,其核心特征是不依赖全局或静态变量、不返回静态数据指针、仅使用参数和局部变量,如int add_numbers(int a, int b);而get_counter因使用静态变量不可重入,可通过将状态交由调用者管理实现可重入;可重入函数必线程安全,但反之未必,如加锁操作虽保障线程安全却因共享状态而非可重入;设计上应避免全局状态、采用纯函数风格、慎用标准库中的非可重入函数,推荐使用如my_strtok这类状态外置的接口,从而确保函数在多线程、信号处理等场景下安全可靠。

c++如何实现一个可重入函数_c++线程安全与函数设计原则

在C++中,实现一个可重入函数并确保线程安全,核心在于避免共享状态的竞态条件,并合理设计函数的行为。可重入函数是指可以在执行过程中被中断,然后在中断点重新进入而不影响结果的函数。它通常也是线程安全的,但两者并不完全等价:可重入是更强的保证。

什么是可重入函数

可重入函数满足以下条件:

  • 不使用全局变量或静态局部变量;
  • 不返回指向静态数据的指针;
  • 不调用不可重入的函数(如某些标准库函数);
  • 所有数据都来自参数或函数上的局部变量。

这样的函数可以被多个线程或递归调用同时执行而不会产生冲突。

如何编写可重入函数

关键在于隔离状态。看一个简单的例子:

立即学习C++免费学习笔记(深入)”;

int add_numbers(int a, int b) {
    return a + b;
}

这个函数是典型的可重入函数:只依赖输入参数,没有副作用。

再看一个非可重入的例子:

int* get_counter() {
    static int counter = 0;
    counter++;
    return &counter; // 返回静态数据地址
}

这个函数不是可重入的,因为使用了静态变量,且返回其地址。多个调用会互相干扰。

改为可重入版本:

bloop
bloop

快速查找代码,基于GPT-4的语义代码搜索

下载
void increment_counter(int* counter) {
    (*counter)++;
}

状态由调用者管理,函数本身无内部状态,因此可重入。

线程安全与可重入的关系

线程安全意味着多个线程并发调用该函数不会导致数据损坏。可重入函数一定是线程安全的,但反过来不一定成立。

例如,使用互斥量保护共享资源的函数可能是线程安全的,但不是可重入的:

#include 

std::mutex mtx; int global_data = 0;

void unsafe_add(int value) { std::lock_guard lock(mtx); global_data += value; // 修改全局变量 }

这个函数是线程安全的,但由于修改了全局状态,不能在信号处理程序中安全调用,也不是可重入的。

函数设计原则

为了提高可重入性和线程安全性,应遵循以下设计原则:

  • 避免使用静态或全局变量:将状态外部化,由调用方传入。
  • 使用纯函数风格:输入确定则输出确定,无副作用。
  • 小心使用C标准库函数:如strtok是非可重入的,应改用strtok_r(POSIX)或现代替代方案。
  • 避免在可重入函数中加锁:加锁可能引入死锁风险,尤其是在信号处理或递归场景中。
  • 资源管理交给调用者:比如缓冲区、上下文对象等,由外部分配和释放。

举个实际例子:实现一个可重入的字符串分割函数:

char* my_strtok(char* str, const char* delim, char** saveptr) {
    char* p;
    if (str != nullptr) {
        *saveptr = str;
    }
    if (*saveptr == nullptr || **saveptr == '\0') {
        return nullptr;
    }
    p = *saveptr;
    while (*(*saveptr) && strchr(delim, **saveptr)) {
        (*saveptr)++;
    }
    p = *saveptr;
    while (**saveptr && !strchr(delim, **saveptr)) {
        (*saveptr)++;
    }
    if (**saveptr) {
        *(*saveptr)++ = '\0';
    }
    return p;
}

这个版本类似于strtok_r,把保存位置放在外部指针中,因此是可重入的。

基本上就这些。可重入函数的设计本质是“无状态”或“状态外置”,配合良好的接口设计,就能自然支持多线程环境,也更容易测试和维护。

相关专题

更多
全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

73

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

96

2025.09.18

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

249

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

205

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1435

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

609

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

547

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

539

2024.04.29

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 0.9万人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号