以下是针对输出流(如 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;
}
优化项
- 单例模式:全局唯一Logger实例。
- 延迟字符串拼接:在锁外构建日志内容,减少临界区耗时。
- 扩展性:可轻松添加日志级别、输出到文件等功能。
方案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';
}
优点:无需手动管理锁,语法简洁。缺点:兼容性要求高。
性能注意事项
- 避免高频小日志:频繁加锁会导致性能下降,建议批量合并日志。
- 禁用
std:endl
:它隐含flush()
操作,改用'\n'
。 - 异步日志库:生产环境推荐使用 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助手提供,问题来源于学员提问