ZBLOG

为输出流使用单独的 mutex 可运行、代码

以下是针对输出流(如 std::cout)使用独立 mutex 的完整可运行代码示例,确保多线程环境下日志/输出的原子性和顺序性


方案1:全局输出锁(基础版)

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

// 全局互斥锁,专用于保护 std::cout
std::mutex g_cout_mutex;

void safe_print(const std::string& message) {
    std::lock_guard<std::mutex> lock(g_cout_mutex); // 自动加锁/解锁
    std::cout << "[Thread " << std::this_thread::get_id() << "] " << message << std::endl;
}

void worker(int id) {
    safe_print("Start working...");
    // 模拟工作耗时
    std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
    safe_print("Job " + std::to_string(id) + " completed.");
}

int main() {
    const int kNumThreads = 5;
    std::vector<std::thread> threads;

    for (int i = 0; i < kNumThreads; ++i) {
        threads.emplace_back(worker, i);
    }

    for (auto& t : threads) {
        t.join();
    }

    return 0;
}

关键点

  • 专用互斥锁g_cout_mutex 仅保护 std::cout,避免与其他业务逻辑锁竞争。
  • RAII管理lock_guard 确保异常安全。
  • 线程标识:输出中包含线程ID便于调试。

方案2:封装为线程安全的Logger类(推荐)

#include <iostream>
#include <mutex>
#include <thread>
#include <sstream>

class ThreadSafeLogger {
public:
    static ThreadSafeLogger& instance() {
        static ThreadSafeLogger logger; // C++11保证静态局部变量线程安全
        return logger;
    }

    void log(const std::string& message) {
        std::lock_guard<std::mutex> lock(mutex_);
        std::cout << "[Thread " << std::this_thread::get_id() << "] " << message << "\n";
        // ⚠️注意:不要频繁flush,可能影响性能
    }

private:
    ThreadSafeLogger() = default; // 单例模式禁止外部构造
    std::mutex mutex_;
};

// 使用宏简化调用(可选)
#define LOG(message) ThreadSafeLogger::instance().log(message)

void worker(int id) {
    LOG("Start working...");
    
    // 模拟复杂日志生成(避免在锁内拼接字符串)
    std::ostringstream oss;
    oss << "Processing item " << id << "/" << (id * 10);
    
     LOG(oss.str()); 
}

int main() {
     const int kNumThreads = 3;
     std:🧵:vector<std:🧵:thread> threads(kNumThreads);

     for (int i=0;i<kNumThreads;++i){
         threads[i]=std:🧵:thread(worker,i);
     }
     
     for(auto&t:threads)t.join();

     return EXIT_SUCCESS;  
}

优化项

  1. 单例模式:全局唯一Logger实例。
  2. 延迟字符串拼接:在锁外构建日志内容,减少临界区耗时。
  3. 扩展性:可轻松添加日志级别、输出到文件等功能。

方案3:C++20的 std:osyncstream(现代替代)

若编译器支持C++20,可直接使用标准库提供的同步流:

#include <syncstream>
#include <iostream>

void worker(int id){
   // ⭐️自动同步输出(底层自带互斥锁)
   std:osyncstream(std:cout)<<"[Thread "<<std:this_thread:.get_id<<"] "
                           <<"Hello from task"<<id<<'\n';  
}

优点:无需手动管理锁,语法简洁。缺点:兼容性要求高。


性能注意事项

  1. 避免高频小日志:频繁加锁会导致性能下降,建议批量合并日志。
  2. 禁用 std:endl:它隐含 flush() 操作,改用 '\n'
  3. 异步日志库:生产环境推荐使用 spdlog等专业库。

编译与运行

# C++11版本编译命令
g++ -std=c++11 -pthread safe_cout.cpp -o safe_cout && ./safe_cout

# C++20版本编译命令(需支持)
g++ -std=c++20 -pthread syncstream.cpp -o syncstream && ./syncstream 

通过独立互斥锁或现代化同步工具,可彻底解决多线程输出的乱序问题。


内容由零声教学AI助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://golang.0voice.com/?id=23979

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?