导读:本期聚焦于小伙伴创作的《C++23中std::print如何实现高级颜色控制与多线程输出原子性保障》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++23中std::print如何实现高级颜色控制与多线程输出原子性保障》有用,将其分享出去将是对创作者最好的鼓励。

C++23标准正式引入了std::print工具,它简化了格式化输出的流程,同时支持更灵活的终端样式控制,并且在多线程场景下的输出处理也有对应的适配方案。和传统的std::cout相比,std::print的格式化语法更清晰,执行效率也有一定提升,逐渐成为C++开发者输出操作的首选工具。

C++23中std::print如何实现高级颜色控制与多线程输出原子性保障

std::print基础用法回顾

std::print定义在<print>头文件中,支持类似Python的格式化字符串语法,不需要手动拼接输出内容。基础的字符串输出示例如下:

#include <print>
#include <string>

int main() {
    std::string name = "C++23";
    int version = 23;
    // 基础格式化输出
    std::print("欢迎使用{},版本号是{}n", name, version);
    return 0;
}

std::print的高级颜色控制实现

std::print本身不直接提供颜色控制接口,但可以通过嵌入ANSI转义序列来实现终端的颜色、样式调整。ANSI转义序列以<ESC>[开头,后面跟随样式参数,最后以字母结尾标识样式类型。

常用ANSI转义序列规则

常见的样式参数和对应的效果如下:

  • 前景色(文字颜色):30-37为基础颜色,90-97为高亮基础颜色
  • 背景色:40-47为基础背景色,100-107为高亮背景色
  • 样式控制:0为重置所有样式,1为加粗,3为斜体,4为下划线

通过std::print输出带颜色的内容时,只需要把对应的转义序列嵌入到格式化字符串中即可,示例代码如下:

#include <print>

int main() {
    // 输出红色加粗文字
    std::print("33[1;31m这是红色加粗的文字33[0mn");
    // 输出绿色背景的白色文字
    std::print("33[47;32m白字绿底内容33[0mn");
    // 输出带下划线的蓝色文字
    std::print("33[4;34m带下划线的蓝色文字33[0mn");
    return 0;
}

封装颜色控制工具函数

为了避免每次手动拼接转义序列,我们可以封装通用的颜色控制函数,提升代码可读性和复用性:

#include <print>
#include <string>

// 前景色枚举
enum class ForeColor {
    RED = 31,
    GREEN = 32,
    YELLOW = 33,
    BLUE = 34,
    MAGENTA = 35,
    CYAN = 36,
    WHITE = 37
};

// 背景色枚举
enum class BackColor {
    RED = 41,
    GREEN = 42,
    YELLOW = 43,
    BLUE = 44,
    MAGENTA = 45,
    CYAN = 46,
    WHITE = 47
};

// 样式枚举
enum class TextStyle {
    RESET = 0,
    BOLD = 1,
    ITALIC = 3,
    UNDERLINE = 4
};

// 生成带样式的字符串
std::string color_text(const std::string& text, ForeColor fore, BackColor back = (BackColor)0, TextStyle style = TextStyle::RESET) {
    // 0表示不使用背景色
    if (back == (BackColor)0) {
        return "33[" + std::to_string((int)style) + ";" + std::to_string((int)fore) + "m" + text + "33[0m";
    }
    return "33[" + std::to_string((int)style) + ";" + std::to_string((int)fore) + ";" + std::to_string((int)back) + "m" + text + "33[0m";
}

int main() {
    std::print("{}", color_text("红色提示信息", ForeColor::RED, (BackColor)0, TextStyle::BOLD));
    std::print("n");
    std::print("{}", color_text("蓝底白字内容", ForeColor::WHITE, BackColor::BLUE, TextStyle::UNDERLINE));
    std::print("n");
    return 0;
}

多线程输出原子性问题与std::print的机制

很多开发者认为std::print比std::cout更安全,实际上在没有额外同步措施的情况下,std::print在多线程下的输出同样不具备原子性。多个线程同时调用std::print输出内容时,不同线程的输出片段可能会交错,导致最终输出内容混乱。

多线程输出错乱示例

下面的代码启动两个线程同时输出内容,运行后会看到输出内容交错:

#include <print>
#include <thread>
#include <chrono>

void print_task(const std::string& prefix) {
    for (int i = 0; i < 3; ++i) {
        std::print("{}: 第{}次输出内容n", prefix, i);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

int main() {
    std::thread t1(print_task, "线程A");
    std::thread t2(print_task, "线程B");
    t1.join();
    t2.join();
    return 0;
}

运行上述代码后,可能会出现类似"线程A: 第0次输出内容n线程B: 第0次输出内容n"的交错情况,不符合预期的输出逻辑。

多线程下std::print输出原子性保障方案

要保证多线程下std::print的输出原子性,核心思路是让同一时刻只有一个线程能执行完整的输出操作,常用的方案有以下几种。

使用互斥锁同步

通过std::mutex对输出操作加锁,是最直接的保障方式,示例代码如下:

#include <print>
#include <thread>
#include <mutex>
#include <chrono>

std::mutex print_mutex;

void safe_print_task(const std::string& prefix) {
    for (int i = 0; i < 3; ++i) {
        // 加锁保证输出原子性
        std::lock_guard<std::mutex> lock(print_mutex);
        std::print("{}: 第{}次输出内容n", prefix, i);
        // 锁自动释放
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

int main() {
    std::thread t1(safe_print_task, "线程A");
    std::thread t2(safe_print_task, "线程B");
    t1.join();
    t2.join();
    return 0;
}

封装原子输出函数

可以把加锁逻辑封装到统一的输出函数中,避免每个调用处都写加锁代码:

#include <print>
#include <thread>
#include <mutex>
#include <string>
#include <chrono>

std::mutex print_mutex;

// 原子输出函数
template <typename... Args>
void atomic_print(const std::string& fmt, Args&&... args) {
    std::lock_guard<std::mutex> lock(print_mutex);
    std::print(fmt, std::forward<Args>(args)...);
}

void task(const std::string& name) {
    for (int i = 0; i < 3; ++i) {
        atomic_print("{}: 执行第{}次操作n", name, i);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

int main() {
    std::thread t1(task, "任务1");
    std::thread t2(task, "任务2");
    t1.join();
    t2.join();
    return 0;
}

使用std::osyncstream(C++23新增)

C++23同时新增了<syncstream>头文件,std::osyncstream可以自动保证输出操作的原子性,不需要手动加锁,使用方式如下:

#include <print>
#include <thread>
#include <syncstream>
#include <chrono>

void sync_print_task(const std::string& prefix) {
    for (int i = 0; i < 3; ++i) {
        // osyncstream自动保障输出原子性
        std::osyncstream(std::cout) << prefix << ": 第" << i << "次输出n";
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

int main() {
    std::thread t1(sync_print_task, "线程A");
    std::thread t2(sync_print_task, "线程B");
    t1.join();
    t2.join();
    return 0;
}

如果需要结合std::print的格式化能力,可以先把内容格式化到字符串,再用std::osyncstream输出,同样能保证原子性。

注意事项

  • ANSI转义序列仅在支持该标准的终端生效,部分老旧终端可能无法显示颜色效果
  • 使用互斥锁保障原子性时,要注意避免死锁,尽量不要在锁内执行耗时操作
  • std::osyncstream的性能比手动加锁略低,在对性能要求极高的场景下需要权衡选择

std::printC++23颜色控制多线程输出原子性保障修改时间:2026-06-14 01:52:06

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。