C++多线程编程:解锁性能与并发的奥秘

通过本文,我们深入了解了C++中的多线程编程,探讨了创建线程、数据同步、原子操作、同步和通信、异步任务与Future/Promise、性能优化与线程池等主题。

今天我们将深入探讨C++中的多线程编程,揭示多线程如何解锁性能潜力,提高程序的并发性能。

什么是多线程?

在计算机科学中,多线程是指一个进程(程序的执行实例)中的多个线程同时执行。每个线程都是程序中独立的控制流,可以执行独立的任务。相比于单线程,多线程能够更有效地利用计算机的多核处理器,提高程序的执行效率。

C++标准库提供了丰富的多线程支持,通过 头文件,我们可以轻松创建和管理多线程。

创建线程,让我们通过一个简单的例子来了解如何在C++中创建线程:

复制

#include <thread>
// 线程执行的函数
void printHello() {std::cout << "Hello from thread!" << std::endl;
}int main() {// 创建线程并启动std::thread myThread(printHello);// 主线程继续执行其他任务//TODO// 等待线程执行完毕myThread.join();return 0;
}

在这个例子中,我们通过 std::thread 类创建了一个新的线程,并传递了要在新线程中执行的函数 printHello。然后,我们使用 join() 函数等待线程执行完毕。

数据共享与同步

多线程编程中,经常会涉及到多个线程同时访问共享数据的情况。这时,需要特别注意数据同步,以避免竞态条件和数据不一致性问题。

C++中提供了 std::mutex(互斥锁)来解决这类问题。让我们看一个简单的例子:

复制


#include <thread>
#include <mutex>std::mutex myMutex;int sharedData = 0;// 线程执行的函数,对共享数据进行操作
void incrementData() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(myMutex); // 使用lock_guard自动管理锁的生命周期sharedData++;}
}int main() {std::thread thread1(incrementData);std::thread thread2(incrementData);thread1.join();thread2.join();std::cout << "Final value of sharedData: " << sharedData << std::endl;return 0;
}

在这个例子中,两个线程并发地增加共享数据 sharedData 的值,通过 std::lock_guard 来确保在同一时刻只有一个线程能够访问共享数据,从而避免竞态条件。

原子操作

C++标准库还提供了 std::atomic 类型,用于执行原子操作,这是一种无需使用互斥锁就能确保操作的完整性的方法。让我们看一个简单的例子:

复制

#include <thread>
#include <atomic>
std::atomic<int> atomicData(0);
// 线程执行的函数,对原子数据进行操作
void incrementAtomicData() {for (int i = 0; i < 100000; ++i) {atomicData++;}
}int main() {std::thread thread1(incrementAtomicData);std::thread thread2(incrementAtomicData);thread1.join();thread2.join();std::cout << "Final value of atomicData: " << atomicData << std::endl;return 0;
}

在这个例子中,我们使用 std::atomic 来声明 atomicData,并在两个线程中并发地增加它的值,而无需使用互斥锁。

同步和通信

在多线程编程中,线程之间的同步和通信是至关重要的。C++中的 std::condition_variable 和 std::unique_lock 提供了一种灵活的方式来实现线程之间的同步和通信。

让我们通过一个简单的生产者-消费者问题的例子来了解它的应用:

复制

#include <thread>
#include <mutex>
#include <condition_variable>std::mutex myMutex;
std::condition_variable myCV;
int sharedData = 0;
bool dataReady = false;// 生产者线程
void produceData() {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lock(myMutex);sharedData = i;dataReady = true;lock.unlock();myCV.notify_one(); // 通知消费者数据已准备好std::this_thread::sleep_for(std::chrono::milliseconds(200));}
}// 消费者线程
void consumeData() {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lock(myMutex);myCV.wait(lock, []{ return dataReady; }); // 等待数据准备好的通知std::cout << "Consumed: " << sharedData << std::endl;dataReady = false;lock.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(500));}
}int main() {std::thread producerThread(produceData);std::thread consumerThread(consumeData);producerThread.join();consumerThread.join();return 0;
}

在这个例子中,生产者线程产生数据并通知消费者线程,消费者线程等待数据准备好的通知后消费数据。这通过 std::condition_variable 和 std::unique_lock 实现了线程之间的同步和通信。

异步任务与Future/Promise

C++标准库还提供了 std::async、std::future 和 std::promise 来支持异步任务和获取任务结果。这种机制允许我们在一个线程中启动任务,然后在另一个线程中获取其结果。

复制

#include <future>// 异步任务函数
int calculateSum(int a, int b) {std::this_thread::sleep_for(std::chrono::milliseconds(2000)); // 模拟耗时操作return a + b;
}int main() {// 启动异步任务std::future<int> resultFuture = std::async(calculateSum, 5, 10);// 主线程继续执行其他任务// 获取异步任务的结果int result = resultFuture.get();std::cout << "Result of asynchronous task: " << result << std::endl;return 0;
}

在这个例子中,std::async 启动了一个异步任务,然后主线程继续执行其他任务。当需要异步任务的结果时,可以通过 get() 函数获取。这使得我们能够更有效地利用计算资源,提高程序的响应性。

性能优化与线程池

为了更好地掌握多线程的性能,我们还可以使用线程池。线程池是一组线程,它们在程序启动时创建,然后在整个程序生命周期内重复使用,从而避免线程创建和销毁的开销。

C++标准库并没有直接提供线程池,但第三方库(如C++11 ThreadPool)提供了简单易用的接口:

复制

#include "ThreadPool.h" // 第三方线程池库
// 任务函数
void printNumber(int number) {std::cout << "Number: " << number << std::endl;
}int main() {ThreadPool pool(4); // 创建包含4个线程的线程池// 提交任务给线程池for (int i = 0; i < 10; ++i) {pool.enqueue(printNumber, i);}// 主线程继续执行其他任务// 等待线程池中的任务完成pool.wait();return 0;
}

在这个例子中,我们使用了一个简单的线程池库,创建了包含4个线程的线程池,并向线程池提交了一系列任务。线程池负责管理任务的执行,从而更好地利用计算资源。

C++多线程编程的注意事项

在使用多线程编程时,需要注意一些关键的事项:

  • 数据同步 确保对共享数据的访问是线程安全的,避免竞态条件和数据不一致性问题。
  • 死锁 小心使用锁,以避免死锁情况。死锁是指两个或多个线程被永久地阻塞,因为每个线程都在等待另一个线程释放某个资源。
  • 线程安全的数据结构 使用线程安全的数据结构,如 std::atomic、std::mutex、std::condition_variable 等,来简化多线程编程。
  • 注意资源管理 确保正确地管理线程所需的资源,避免资源泄漏和不必要的性能开销。
  • 适度并行 并不是所有的任务都适合并行执行。在选择使用多线程时,需要仔细评估任务的性质和程序的整体结构。
结语

通过本文,我们深入了解了C++中的多线程编程,探讨了创建线程、数据同步、原子操作、同步和通信、异步任务与Future/Promise、性能优化与线程池等主题。多线程编程为我们提供了一种强大的工具,可以充分利用多核处理器,提高程序的性能和并发性能。

技术前沿拓展

前端开发,你的认知不能仅局限于技术内,需要发散思维了解技术圈的前沿知识。细心的人会发现,开发内部工具的过程中,大量的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的大量时间。

介绍一款程序员都应该知道的软件JNPF快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。如果你有闲暇时间,可以做个知识拓展。

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/451981.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

河西走廊潜在蒸散发时空格局变化与气象因素的关系_马亚丽_2023

河西走廊潜在蒸散发时空格局变化与气象因素的关系_马亚丽_2023 摘要关键词 1 数据与方法1.1 数据来源1.2 变化趋势分析1.3 定性分析方法1.3.1 主成分分析1.3.2 相关系数1.3.3 通径分析 1.4 定量分析方法1.4.1 敏感系数1.4.2 贡献率计算 2 结果与分析2.1 ET0多年变化特征2.1.1 E…

乐意购项目前端开发 #6

一、商品详情页面 代码模版 创建Detail文件夹, 然后创建index.vue文件 <script setup> import { getDetail } from "/api/goods/index"; import { ref, onMounted } from "vue"; import { useRoute } from "vue-router"; import { useCar…

正点原子--STM32定时器学习笔记(2)

书接上文&#xff0c;本篇是对基本定时器实验部分进行总结&#xff01; 实验目标&#xff1a;通过TIM6基本定时器定时500ms&#xff0c;让LED0每隔500ms闪烁。 解决思路&#xff1a;使用定时器6&#xff0c;实现500ms产生一次定时器更新中断&#xff0c;在中断里执行“翻转LED0…

【FX110网】日交所发布1月交易数据:衍生品交易额达历年1月最高!

日本交易所集团&#xff08;日交所&#xff0c;JPX&#xff09;发布了其2024年1月的交易数据概览。数据显示&#xff0c;该交易所当月衍生品交易额创新历年来的1月交易数据最高纪录。2024年1月共有19个交易日。 2024年1月交易概览现货股票市场 2024年1月&#xff0c;该交易所主…

浅谈QT的几种线程的使用和区别。

简介&#xff1a; 线程是操作系统中的基本执行单元&#xff0c;是一个独立的执行路径。每个线程都有自己的栈空间&#xff0c;用于存储本地变量和函数调用的上下文。多个线程可以在同一进程中并发执行&#xff0c;从而实现并发处理&#xff0c;提高程序的性能和响应能力。 与进…

2020年通信工程师初级专业实务真题

文章目录 一、第1章 现代通信网概述&#xff1a;信令网、同步网、管理网。第10章 通信业务&#xff1a;通信产业链&#xff0c;通信终端的分类&#xff0c;通信业务的定义及分类二、第3章 接入网&#xff1a;无线接入网的优点&#xff0c;接入网的接口&#xff08;UNI&#xff…

13.从桥接模式细品人生的几座桥

“物理学不存在了&#xff0c;今后也不会存在。”——《三体》 在《三体》中&#xff0c;有这样一个桥段&#xff0c;顶级的物理学家杨冬在三体文明超级计算机“智子”的干扰和误导下&#xff0c;得出了物理实验的结果在实验之前就会被某种力量确定的结论&#xff0c;导致自己…

Linux进程信号处理:深入理解与应用(2​​)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;its 6pm but I miss u already.—bbbluelee 0:01━━━━━━️&#x1f49f;──────── 3:18 &#x1f504; ◀️…

MySQL数据库①_MySQL入门(概念+使用)

目录 1. 数据库的概念 1.1 数据库的存储介质 1.2 主流数据库 2. MySQL的基本使用 2.1 链接数据库 2.2 服务器管理 2.3 数据库&#xff0c;服务器和表关系 2.4 简单MySQL语句 3. MySQL架构 4. SQL分类 5. 存储引擎 本篇完。 1. 数据库的概念 数据库是按照数据结构来…

华为nova12系列:图片HDR显示,让你的照片全面升级!

你是不是也想给自己的照片加点料&#xff0c;让它们看起来更真实、捕捉到更多的细节和光影&#xff1f;不用愁&#xff0c;华为nova12系列就为你量身打造了图片HDR显示技术&#xff0c;让你的照片从此焕发绚丽光芒&#xff01; 回忆一下&#xff0c;在节日的夜晚想拍下绚丽的灯…

【Java】Redis入门

1. Redis入门 1.1 Redis简介 Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件。 **官网&#xff1a;**https://redis.io **中文网&#xff1a;**https://www.redis.net.cn/ key-value结构存储&#xff1a; 主要特点&#xff1a…

数据结构(C语言)代码实现(六)——单链表的实现

目录 参考、格式 头文件LinkList.h 一、将函数的小括号写成中括号 二、读取权限冲突 三、L->Last指针没有移动 四、函数指针的使用 头文件完整代码 测试函数&#xff08;主函数&#xff09;test.cpp 测试结果 参考、格式 数据结构课本2.3节&#xff08;严蔚敏版&a…