2026/4/6 7:33:10
网站建设
项目流程
网络服务公司有哪些,驻马店网站优化,企业网站html源代码,asp程序网站后台发布产品的时候前台怎么不显示产品名称提示#xff1a;文章写完后#xff0c;目录可以自动生成#xff0c;如何生成可参考右边的帮助文档 文章目录C多线程使用注意点及示例一、C多线程使用核心注意点1. 环境与基础要求2. 线程的创建与销毁3. 数据竞争#xff08;竞态条件#xff09;4. 线程同步机制5. 死锁6. 参…提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档文章目录C多线程使用注意点及示例一、C多线程使用核心注意点1. 环境与基础要求2. 线程的创建与销毁3. 数据竞争竞态条件4. 线程同步机制5. 死锁6. 参数传递与资源生命周期7. 异常处理8. 线程数量二、示例1简单多线程使用基础用法三、示例2多线程加锁同步解决数据竞争1. 未加锁的情况数据竞争结果错误2. 加锁的情况使用std::mutexstd::lock_guard结果正确总结C多线程使用注意点及示例C11 引入了thread库正式支持原生多线程编程。多线程编程的核心是线程管理和共享资源同步以下是关键注意点以及两个示例基础多线程用法、多线程加锁同步。一、C多线程使用核心注意点1. 环境与基础要求C11及以上标准编译器需支持如GCC 4.8、MSVC 2012、Clang 3.3。编译时需链接线程库如GCC需加-pthread参数。2. 线程的创建与销毁std::thread对象创建时立即启动线程需传入可调用对象函数、lambda、函数对象、类成员函数等。必须对std::thread对象调用join()等待线程结束或detach()分离线程后台运行否则析构时会触发std::terminate()导致程序崩溃。detach()后的线程由系统接管无法再控制需确保线程访问的资源生命周期足够避免悬垂引用。3. 数据竞争竞态条件多个线程同时访问共享数据且至少有一个是写操作时会出现数据竞争导致未定义行为结果错误、程序崩溃等。必须通过同步机制互斥锁、原子操作、条件变量等保护共享数据。4. 线程同步机制互斥锁std::mutex保护临界区同一时间只有一个线程进入。推荐使用RAII风格的std::lock_guard自动加锁/解锁或std::unique_lock更灵活支持手动加锁、超时、与条件变量配合避免手动解锁遗漏如异常时。原子操作std::atomic适用于简单数值操作如计数器比互斥锁更高效底层由CPU指令保证原子性。条件变量std::condition_variable用于线程间通信如等待某个条件满足后再执行。5. 死锁多个线程互相持有对方需要的锁导致无限等待。避免方式按固定顺序加锁、使用std::lock同时加多个锁、使用C17的std::scoped_lockRAII风格多锁、设置锁超时等。6. 参数传递与资源生命周期std::thread传递参数时默认是值拷贝若需传递引用需用std::ref/std::cref包装。线程函数中若访问主线程的局部变量需确保变量生命周期长于线程否则会出现悬垂引用。7. 异常处理线程函数内的未捕获异常会导致程序终止std::terminate()需在线程内捕获所有异常。8. 线程数量计算密集型任务线程数建议等于CPU核心数避免过多上下文切换。IO密集型任务线程数可远大于CPU核心数利用IO等待时间。二、示例1简单多线程使用基础用法该示例展示线程创建、参数传递值/引用、join()等待线程结束、lambda表达式作为线程函数。#includeiostream#includethread#includestring#includefunctional// std::ref// 普通函数打印数字voidprintNumbers(intn,conststd::stringprefix){for(inti1;in;i){std::coutprefix: istd::endl;// 模拟耗时操作让线程切换更明显std::this_thread::sleep_for(std::chrono::milliseconds(100));}}// 类成员函数示例classMyClass{public:voidprintChars(charc,inttimes){for(inti1;itimes;i){std::coutChar: 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::refintnum3;std::string strThread3;std::threadt3([](intcount,conststd::strings){for(inti1;icount;i){std::couts: Lambda - istd::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;}编译运行GCCg -stdc11 thread_basic.cpp -o thread_basic -pthread ./thread_basic说明三个线程会交替执行输出顺序不固定由操作系统调度。线程3使用lambda表达式通过std::ref传递引用参数避免值拷贝。三、示例2多线程加锁同步解决数据竞争该示例展示多个线程修改共享变量时的数据竞争问题以及使用std::mutexstd::lock_guard解决同步问题同时对比原子操作的方案。1. 未加锁的情况数据竞争结果错误#includeiostream#includethread#includevector// 共享变量intg_count0;// 累加函数未加锁voidincrement(inttimes){for(inti0;itimes;i){// 非原子操作读取g_count → 加1 → 写回g_count// 多个线程同时执行时会出现数据覆盖g_count;// 模拟耗时操作std::this_thread::sleep_for(std::chrono::nanoseconds(1));}}intmain(){constintthread_num5;// 5个线程constinttimes_per_thread1000;// 每个线程累加1000次std::vectorstd::threadthreads;// 创建线程for(inti0;ithread_num;i){threads.emplace_back(increment,times_per_thread);}// 等待线程结束for(autot:threads){t.join();}// 预期结果5*10005000但实际结果会小于5000数据竞争std::cout最终count值未加锁g_countstd::endl;return0;}2. 加锁的情况使用std::mutexstd::lock_guard结果正确#includeiostream#includethread#includevector#includemutex// std::mutex, std::lock_guard// 共享变量intg_count0;// 互斥锁保护g_countstd::mutex g_mutex;// 累加函数加锁voidincrement(inttimes){for(inti0;itimes;i){// RAII风格lock_guard构造时加锁析构时解锁即使发生异常也会解锁std::lock_guardstd::mutexlock(g_mutex);// 临界区同一时间只有一个线程执行g_count;// 模拟耗时操作可放在锁外减少锁的持有时间提高性能// std::this_thread::sleep_for(std::chrono::nanoseconds(1));}}// 可选原子操作方案更高效适用于简单数值操作#includeatomicstd::atomicintg_atomic_count(0);voidincrement_atomic(inttimes){for(inti0;itimes;i){g_atomic_count;// 原子操作无需加锁std::this_thread::sleep_for(std::chrono::nanoseconds(1));}}intmain(){constintthread_num5;constinttimes_per_thread1000;std::vectorstd::threadthreads;// 方案1使用互斥锁for(inti0;ithread_num;i){threads.emplace_back(increment,times_per_thread);}for(autot:threads){t.join();}std::cout最终count值加锁g_countstd::endl;// 输出5000// 方案2使用原子操作清空线程容器重新测试threads.clear();for(inti0;ithread_num;i){threads.emplace_back(increment_atomic,times_per_thread);}for(autot:threads){t.join();}std::cout最终count值原子操作g_atomic_countstd::endl;// 输出5000return0;}编译运行GCCg -stdc11 thread_lock.cpp -o thread_lock -pthread ./thread_lock说明std::lock_guard是RAII风格的锁管理避免了手动调用lock()和unlock()的遗漏问题如异常时。临界区应尽可能小如将耗时操作放在锁外以减少线程阻塞提高并发性能。简单的数值操作如计数器使用std::atomic更高效无需互斥锁复杂的临界区如多个操作组成的逻辑使用互斥锁。总结C多线程编程的关键是线程管理正确使用join()/detach()和共享资源同步避免数据竞争合理使用互斥锁、原子操作、条件变量同时需注意死锁和资源生命周期问题。上述示例覆盖了基础用法和核心同步场景可作为多线程编程的入门参考。