当前位置: 首页 > news >正文

C++中的多线程编程及线程同步

文章目录

  • 前言
  • 多线程的核心价值
  • C++多线程编程基础
  • 线程同步机制
  • 总结

前言

在当今计算领域,无论是追求极致响应速度的桌面应用、处理海量并发请求的服务器后端,还是需要实时处理数据的科学计算与游戏引擎,多线程技术已然成为提升软件性能与用户体验的核心手段。它通过“分而治之”的策略,将应用程序的任务负载分配到多个执行流中,从而最大限度地挖掘现代多核处理器的并行计算潜力,对于提高软件的流畅度、响应能力和整体执行效率具有不可替代的重要作用。

本文旨在系统地介绍C++语言中的多线程编程,并给出案例。

多线程的核心价值

  • 提升性能与吞吐量:在拥有多个CPU核心的系统中,单线程程序只能利用其中一个核心,造成巨大的计算资源浪费。多线程程序可以将计算密集型任务(如图像处理、数据编码、物理模拟)分解成多个子任务,并由多个线程并行执行,从而显著缩短任务总耗时,实现近乎线性的性能加速。

  • 增强响应性与流畅度:在图形用户界面(GUI)应用程序中,如果将耗时操作(如文件读写、网络请求)放在主线程(通常是UI线程)中执行,会导致界面“冻结”,无法响应用户操作。通过创建后台工作线程来处理这些阻塞性任务,可以确保UI线程始终保持流畅的交互响应,从而极大提升用户体验。

  • 简化异步任务模型:对于需要同时处理多个I/O操作(如网络通信、数据库访问)的服务端程序,多线程模型比传统的异步回调模型更直观、更易于理解和编码。每个连接可以分配一个独立的线程,使得代码逻辑清晰,接近于同步编程的思维方式。

C++多线程编程基础

在C++11标准之前,多线程编程严重依赖平台特定的API。C++11的引入将多线程支持纳入标准库,带来了可移植且类型安全的线程管理工具。

创建线程并启动

#include <iostream>
#include <thread>// 1. 普通函数作为线程入口
void background_task(int id) {std::cout << "线程 " << id << " 正在执行,线程ID: " << std::this_thread::get_id() << std::endl;
}// 2. Lambda表达式作为线程入口
auto lambda_task = [](const std::string& message) {std::cout << "Lambda线程: " << message << std::endl;
};int main() {// 创建并启动线程std::thread t1(background_task, 1); // 传递函数指针和参数std::thread t2(lambda_task, "Hello from Lambda!"); // 传递Lambda和参数// 等待线程完成 (重要!)t1.join(); // 主线程阻塞,直到t1执行完毕t2.join(); // 主线程阻塞,直到t2执行完毕std::cout << "主线程结束。" << std::endl;return 0;
}

输出:
在这里插入图片描述

线程同步机制

当多个线程需要访问共享数据或资源时,如果不加控制,就会引发数据竞争,导致程序行为不确定、崩溃或产生错误结果。线程同步机制正是为了解决这一问题而生的,有以下几种方式:

1. 互斥锁std::mutex

互斥锁是最基本的同步原语,它保证了同一时间只有一个线程可以进入被保护的代码段(临界区)

#include <thread>
#include <mutex>
#include <vector>
#include <iostream>std::mutex g_mutex; // 全局互斥锁
int shared_counter = 0;void increment_counter(int iterations) {for (int i = 0; i < iterations; ++i) {g_mutex.lock();   // 进入临界区前加锁++shared_counter; // 安全地修改共享数据g_mutex.unlock(); // 离开临界区后解锁}
}int main() {std::thread t1(increment_counter, 100000);std::thread t2(increment_counter, 100000);t1.join();t2.join();std::cout << "最终计数器值: " << shared_counter << std::endl; // 正确输出 200000return 0;
}

输出:

在这里插入图片描述

这种方式可能存在的问题:直接使用lock()和unlock()容易因异常或提前返回而导致锁无法释放,造成死锁

2. 锁守卫std::lock_guardstd::unique_lock,可在一定程度上解决上述死锁问题

区别:

  • lock_guard 是基于互斥锁 std::mutex 实现的,unique_lock 是基于通用锁 std::unique_lock 实现。
  • unique_lock 可以实现比 lock_guard 更灵活的锁操作:lock_guard 是不可移动的(moveable),即不能拷贝、赋值、移动,只能通过构造函数初始化和析构函数销毁,unique_lock 是可移动的,可以拷贝、赋值、移动。
  • unique_lock 提供了更多的控制锁的行为,比如锁超时、不锁定、条件变量等。ORB-SLAM算法中常用这个
  • unique_locklock_guard 更重,因为它有更多的功能,更多的开销。如果只需要简单的互斥保护,使用 lock_guard 更好。

lock_guard 案例:

#include <thread>
#include <mutex>
#include <vector>
#include <iostream>std::mutex g_mutex; // 全局互斥锁
int shared_counter = 0;void safe_increment(int iterations) {for (int i = 0; i < iterations; ++i) {std::lock_guard<std::mutex> lock(g_mutex); // 构造即加锁++shared_counter;// lock 析构时自动解锁}
}int main() {// 使用安全的增量函数shared_counter = 0; // 重置计数器std::thread t1(safe_increment, 100000);std::thread t2(safe_increment, 100000);t1.join();t2.join();std::cout << "最终计数器值: " << shared_counter << std::endl; // 正确输出 200000return 0;
}

输出
在这里插入图片描述
unique_lock案例:

#include <iostream>
#include <mutex>
#include <thread>std::mutex g_mutex; // 全局互斥锁
int shared_counter = 0;void worker(int iterations)
{std::lock_guard<std::mutex> lg(g_mutex);  // lock_guard 方式上锁for (int i = 0; i < iterations; ++i) {++shared_counter; // 安全地修改共享数据}std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "worker thread is done." << std::endl;
}  // lock_guard 不支持手动解锁,会在此自动释放锁void another_worker(int iterations)
{std::unique_lock<std::mutex> ul(g_mutex);  // unique_lock 方式上锁for (int i = 0; i < iterations; ++i) {++shared_counter; // 安全地修改共享数据}std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "another worker thread is done." << std::endl;ul.unlock();  // 手动释放锁//do something...
}  // 如果锁未释放,unique_lock 会在此自动释放锁int main()
{std::thread t1(worker, 100000);std::thread t2(another_worker, 100000);t1.join();t2.join();std::cout << "最终计数器值: " << shared_counter << std::endl; // 正确输出 200000return 0;
}

输出:

在这里插入图片描述

3. 条件变量std::condition_variable

条件变量用于实现线程间的等待与通知机制,允许一个线程等待某个条件成立,而另一个线程在条件改变时通知等待的线程。这是实现生产者-消费者模型等协作模式的关键。

#include <queue>
#include <condition_variable>
#include <thread>
#include <mutex>
#include <iostream>std::queue<int> data_queue;
std::mutex queue_mutex;
std::condition_variable data_cond;// 生产者线程
void data_producer() {for (int i = 0; i < 10; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(100));{std::lock_guard<std::mutex> lock(queue_mutex);data_queue.push(i);std::cout << "生产数据: " << i << std::endl;}data_cond.notify_one(); // 通知一个等待的消费者}
}// 消费者线程
void data_consumer() {while (true) {std::unique_lock<std::mutex> lock(queue_mutex);// 等待条件成立:队列非空。等待时会自动释放锁,被唤醒后重新获取锁。data_cond.wait(lock, []{ return !data_queue.empty(); });int data = data_queue.front();data_queue.pop();lock.unlock(); // 尽早释放锁std::cout << "消费数据: " << data << std::endl;if (data == 9) break; // 结束条件}
}int main() {std::thread producer(data_producer);std::thread consumer(data_consumer);producer.join();consumer.join();return 0;
}

输出

在这里插入图片描述

4. 原子操作std::atomic

对于简单的计数器、标志位等,使用互斥锁开销过大。C++提供了std::atomic模板,能够保证对该变量的操作是不可分割的,无需显式加锁,性能极高

#include <atomic>
#include <thread>
#include <iostream>std::atomic<int> atomic_counter(0);void atomic_increment(int iterations) {for (int i = 0; i < iterations; ++i) {++atomic_counter; // 原子操作,线程安全}
}int main() {std::thread t1(atomic_increment, 100000);std::thread t2(atomic_increment, 100000);t1.join();t2.join();// 正确输出 200000std::cout << "最终计数器值: " << atomic_counter.load() << std::endl;return 0;
}

输出

在这里插入图片描述

5. 异步线程std::future
std::future:提供了一种更高级的异步任务执行和结果获取方式,它抽象了线程管理的细节,让开发者更专注于任务本身。

#include <future>
#include <iostream>
#include <thread>// 模拟一个耗时的计算任务
int compute_heavy_task() {std::this_thread::sleep_for(std::chrono::seconds(2));return 42;
}int main() {// 异步启动任务std::future<int> result = std::async(std::launch::async, compute_heavy_task);// ... 主线程可以同时做其他工作 ...for(int i = 0; i < 5; ++i) {std::cout << "主线程工作中..." << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(500));}// 获取异步任务结果(如果需要,会阻塞等待)int value = result.get();std::cout << "异步任务结果为: " << value << std::endl;return 0;
}

在这里插入图片描述

总结

多线程编程是C++开发者迈向高性能应用开发的必经之路。它通过并行化极大地提升了软件的执行效率,并通过将阻塞操作移至后台显著增强了应用的流畅度。C++标准库提供了一套强大而全面的工具集,从基础的std::thread到关键的同步原语(mutex, condition_variable),再到高效的std::atomic。然而,线程引入了复杂性,尤其是数据竞争和死锁问题。成功的关键在于深刻理解并正确运用线程同步机制

http://www.dtcms.com/a/434636.html

相关文章:

  • 湛江做网站从微信运营方案
  • 伊吖学C笔记(8、结构体、链表、union、enum、typedef)
  • 2022 年真题配套词汇单词笔记(考研真相)
  • HTML5消费收入矩阵计算器
  • 霸州做阿里巴巴网站庆安建设局网站
  • PCB学习——STM32F103VET6-STM32主控部分
  • 大学生作业做网站网站建设公司比较
  • 写一个星河社区aistudio大模型部署之后的AI agent转发程序
  • 网站上的中英文切换是怎么做的wordpress页面移动端
  • 八、Scala 集合与函数式编程
  • 腾讯CODING Maven的aar制品添加上传流程
  • Effective Modern C++ 条款29: 移动语义的局限性与实践指南
  • 2025年渗透测试面试题总结-98(题目+回答)
  • 深圳网站制作 公司wordpress备份百度云
  • 《时序数据监控平台优化指南:从查询超时到秒级响应,指标下的存储与检索重构实践》
  • 新版android studio创建项目的一些问题
  • 做企业网站有哪些好处软件技术买什么笔记本好
  • 【Redis】Redis的5种核心数据结构和实战场景对应(在项目中的用法)
  • Vue 与 React 深度对比:技术差异、选型建议与未来趋势
  • 创意网站页面wordpress预约小程序
  • Android_framework-odex优化
  • RAG核心特性:文档过滤和检索
  • 26.awk 使用手册
  • AI应用开发新范式:从模型API到交互式网页的极速实现路径
  • 网站建设2017主流代码语言太原百度快照优化排名
  • Python学习之day02学习(函数模块的上传、数据类型+)
  • 可以下载的建站网站河南省建设厅门户网站
  • [创业之路-661]:采集狩猎社会的主要技术、技术产业链以及产要产品
  • 做图文的网站传媒公司名字大全免费
  • 网站开发和游戏开发哪个好网站怎么架设