原神祈愿记录导出工具:3分钟掌握你的抽卡命运
2026/4/14 14:54:12
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
C++11 引入了<thread>库,正式支持原生多线程编程。多线程编程的核心是线程管理和共享资源同步,以下是关键注意点,以及两个示例(基础多线程用法、多线程加锁同步)。
-pthread参数)。std::thread对象创建时立即启动线程,需传入可调用对象(函数、lambda、函数对象、类成员函数等)。std::thread对象调用join()(等待线程结束)或detach()(分离线程,后台运行),否则析构时会触发std::terminate()导致程序崩溃。detach()后的线程由系统接管,无法再控制,需确保线程访问的资源生命周期足够(避免悬垂引用)。std::lock_guard(自动加锁/解锁)或std::unique_lock(更灵活,支持手动加锁、超时、与条件变量配合),避免手动解锁遗漏(如异常时)。std::lock同时加多个锁、使用C++17的std::scoped_lock(RAII风格多锁)、设置锁超时等。std::thread传递参数时默认是值拷贝,若需传递引用,需用std::ref/std::cref包装。std::terminate()),需在线程内捕获所有异常。该示例展示:线程创建、参数传递(值/引用)、join()等待线程结束、lambda表达式作为线程函数。
#include<iostream>#include<thread>#include<string>#include<functional>// std::ref// 普通函数:打印数字voidprintNumbers(intn,conststd::string&prefix){for(inti=1;i<=n;++i){std::cout<<prefix<<": "<<i<<std::endl;// 模拟耗时操作(让线程切换更明显)std::this_thread::sleep_for(std::chrono::milliseconds(100));}}// 类成员函数示例classMyClass{public:voidprintChars(charc,inttimes){for(inti=1;i<=times;++i){std::cout<<"Char: "<<c<<" ("<<i<<")"<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(150));}}};intmain(){// 1. 线程1:调用普通函数,传递值参数std::threadt1(printNumbers,5,"Thread1");// 2. 线程2:调用类成员函数,需传入对象指针/引用 + 函数参数MyClass obj;std::threadt2(&MyClass::printChars,&obj,'A',4);// 3. 线程3:使用lambda表达式,传递引用参数(需std::ref)intnum=3;std::string str="Thread3";std::threadt3([&](intcount,conststd::string&s){for(inti=1;i<=count;++i){std::cout<<s<<": Lambda - "<<i<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(200));}},std::ref(num),std::ref(str));// std::ref传递引用// 等待所有线程结束(必须调用join,否则析构时崩溃)t1.join();t2.join();t3.join();std::cout<<"所有线程执行完毕!"<<std::endl;return0;}编译运行(GCC):
g++ -std=c++11 thread_basic.cpp -o thread_basic -pthread ./thread_basic说明:
std::ref传递引用参数,避免值拷贝。该示例展示:多个线程修改共享变量时的数据竞争问题,以及使用std::mutex+std::lock_guard解决同步问题,同时对比原子操作的方案。
#include<iostream>#include<thread>#include<vector>// 共享变量intg_count=0;// 累加函数(未加锁)voidincrement(inttimes){for(inti=0;i<times;++i){// 非原子操作:读取g_count → 加1 → 写回g_count// 多个线程同时执行时,会出现数据覆盖g_count++;// 模拟耗时操作std::this_thread::sleep_for(std::chrono::nanoseconds(1));}}intmain(){constintthread_num=5;// 5个线程constinttimes_per_thread=1000;// 每个线程累加1000次std::vector<std::thread>threads;// 创建线程for(inti=0;i<thread_num;++i){threads.emplace_back(increment,times_per_thread);}// 等待线程结束for(auto&t:threads){t.join();}// 预期结果:5*1000=5000,但实际结果会小于5000(数据竞争)std::cout<<"最终count值(未加锁):"<<g_count<<std::endl;return0;}#include<iostream>#include<thread>#include<vector>#include<mutex>// std::mutex, std::lock_guard// 共享变量intg_count=0;// 互斥锁:保护g_countstd::mutex g_mutex;// 累加函数(加锁)voidincrement(inttimes){for(inti=0;i<times;++i){// RAII风格:lock_guard构造时加锁,析构时解锁(即使发生异常也会解锁)std::lock_guard<std::mutex>lock(g_mutex);// 临界区:同一时间只有一个线程执行g_count++;// 模拟耗时操作(可放在锁外,减少锁的持有时间,提高性能)// std::this_thread::sleep_for(std::chrono::nanoseconds(1));}}// 可选:原子操作方案(更高效,适用于简单数值操作)#include<atomic>std::atomic<int>g_atomic_count(0);voidincrement_atomic(inttimes){for(inti=0;i<times;++i){g_atomic_count++;// 原子操作,无需加锁std::this_thread::sleep_for(std::chrono::nanoseconds(1));}}intmain(){constintthread_num=5;constinttimes_per_thread=1000;std::vector<std::thread>threads;// 方案1:使用互斥锁for(inti=0;i<thread_num;++i){threads.emplace_back(increment,times_per_thread);}for(auto&t:threads){t.join();}std::cout<<"最终count值(加锁):"<<g_count<<std::endl;// 输出5000// 方案2:使用原子操作(清空线程容器,重新测试)threads.clear();for(inti=0;i<thread_num;++i){threads.emplace_back(increment_atomic,times_per_thread);}for(auto&t:threads){t.join();}std::cout<<"最终count值(原子操作):"<<g_atomic_count<<std::endl;// 输出5000return0;}编译运行(GCC):
g++ -std=c++11 thread_lock.cpp -o thread_lock -pthread ./thread_lock说明:
std::lock_guard是RAII风格的锁管理,避免了手动调用lock()和unlock()的遗漏问题(如异常时)。std::atomic更高效,无需互斥锁;复杂的临界区(如多个操作组成的逻辑)使用互斥锁。C++多线程编程的关键是线程管理(正确使用join()/detach())和共享资源同步(避免数据竞争,合理使用互斥锁、原子操作、条件变量),同时需注意死锁和资源生命周期问题。上述示例覆盖了基础用法和核心同步场景,可作为多线程编程的入门参考。