当前位置:首页 > 数码 > 揭秘性能与并发的机密-C-多线程编程 (性能与什么有关)

揭秘性能与并发的机密-C-多线程编程 (性能与什么有关)

admin2个月前 (04-14)数码18

当天咱们将深化讨论C++中的多线程编程,提醒多线程如何解锁性能后劲,提高程序的并发性能。

什么是多线程?

在计算机迷信中,多线程是指一个进程(程序的口头实例)中的多个线程同时口头。每个线程都是程序中独立的控制流,可以口头独立的义务。相比于复线程,多线程能够更有效地利用计算机的多核处置器,提高程序的口头效率。

C++规范库提供了丰盛的多线程支持,经过头文件,咱们可以轻松创立和治理多线程。

创立线程,让咱们经过一个方便的例子来了解如何在C++中创立线程:

C#include<thread>//线程口头的函数voidprintHello(){std::cout<<"Hellofromthread!"<<std::endl;}intmn(){//创立线程并启动std::threadmyThread(printHello);//主线程继续口头其余义务//TODO//期待线程口头终了myThread.join();return0;}

在这个例子中,咱们经过std::thread类创立了一个新的线程,并传递了要在新线程中口头的函数printHello。而后,咱们经常使用join()函数期待线程口头终了。

数据共享与同步

多线程编程中,经常会触及到多个线程同时访问共享数据的状况。这时,须要特意留意数据同步,以防止竞态条件和数据不分歧性疑问。

C++中提供了std::mutex(互斥锁)来处置这类疑问。让咱们看一个方便的例子:

#include<thread>#include<mutex>std::mutexmyMutex;intsharedData=0;//线程口头的函数,对共享数据启动操作voidincrementData(){for(inti=0;i<100000;++i){std::lock_guard<std::mutex>lock(myMutex);//经常使用lock_guard智能治理锁的生命周期sharedData++;}}intmain(){std::threadthread1(incrementData);std::threadthread2(incrementData);thread1.join();thread2.join();std::cout<<"FinalvalueofsharedData:"<<sharedData<<std::endl;return0;}

在这个例子中,两个线程并发地参与共享数据sharedData的值,经过std::lock_guard来确保在同一时辰只要一个线程能够访问共享数据,从而防止竞态条件。

原子操作

C++规范库还提供了std::atomic类型,用于口头原子操作,这是一种无需经常使用互斥锁就能确保操作的完整性的方法。让咱们看一个方便的例子:

#include<thread>#include<atomic>std::atomic<int>atomicData(0);//线程口头的函数,对原子数据启动操作voidincrementAtomicData(){for(inti=0;i<100000;++i){atomicData++;}}intmain(){std::threadthread1(incrementAtomicData);std::threadthread2(incrementAtomicData);thread1.join();thread2.join();std::cout<<"FinalvalueofatomicData:"<<atomicData<<std::endl;return0;}

在这个例子中,咱们经常使用std::atomic来申明atomicData,并在两个线程中并发地参与它的值,而无需经常使用互斥锁。

同步和通讯

在多线程编程中,线程之间的同步和通讯是至关关键的。C++中的std::condition_variable和std::unique_lock提供了一种灵敏的模式来成功线程之间的同步和通讯。

让咱们经过一个方便的消费者-消费者疑问的例子来了解它的运行:

#include<thread>#include<mutex>#include<condition_variable>std::mutexmyMutex;std::condition_variablemyCV;intsharedData=0;bool>#include<future>//异步义务函数intcalculateSum(inta,intb){std::this_thread::sleep_for(std::chrono::milliseconds(2000));//模拟耗时操作returna+b;}intmain(){//启动异步义务std::future<int>resultFuture=std::async(calculateSum,5,10);//主线程继续口头其余义务//失掉异步义务的结果intresult=resultFuture.get();std::cout<<"Resultofasynchronoustask:"<<result<<std::endl;return0;}

在这个例子中,std::async启动了一个异步义务,而后主线程继续口头其余义务。当须要异步义务的结果时,可以经过get()函数失掉。这使得咱们能够更有效地利用计算资源,提高程序的照应性。

性能优化与线程池

为了更好地把握多线程的性能,咱们还可以经常使用线程池。线程池是一组线程,它们在程序启动时创立,而后在整个程序生命周期内重复经常使用,从而防止线程创立和销毁的开支。

C++规范库并没有间接提供线程池,但第三方库(如C++11ThreadPool)提供了方便易用的接口:

#include"ThreadPool.h"//第三方线程池库//义务函数voidprintNumber(intnumber){std::cout<<"Number:"<<number<<std::endl;}intmain(){ThreadPoolpool(4);//创立蕴含4个线程的线程池//提交义务给线程池for(inti=0;i<10;++i){pool.enqueue(printNumber,i);}//主线程继续口头其余义务//期待线程池中的义务成功pool.wait();return0;}

在这个例子中,咱们经常使用了一个方便的线程池库,创立了蕴含4个线程的线程池,并向线程池提交了一系列义务。线程池担任治理义务的口头,从而更好地利用计算资源。

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

在经常使用多线程编程时,须要留意一些关键的事项:

结语

经过本文,咱们深化了解了C++中的多线程编程,讨论了创立线程、数据同步、原子操作、同步和通讯、异步义务与Future/Promise、性能优化与线程池等主题。多线程编程为咱们提供了一种弱小的工具,可以充沛应用多核处置器,提高程序的性能和并发性能。


Python 的 GIL 是什么鬼,多线程性能究竟如何

GIL是什么首先需要明确的一点是 GIL 并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。 就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。 有名的编译器例如GCC,INTEL C++,Visual C++等。 Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。 像其中的JPython就没有GIL。 然而因为CPython是大部分环境下默认的Python执行环境。 所以在很多人的概念里CPython就是Python,也就想当然的把 GIL 归结为Python语言的缺陷。 所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL那么CPython实现中的GIL又是什么呢?GIL全称 Global Interpreter Lock 为了避免误导,我们还是来看一下官方给出的解释:In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)好吧,是不是看上去很糟糕?一个防止多线程并发执行机器码的一个Mutex,乍一看就是个BUG般存在的全局锁嘛!别急,我们下面慢慢的分析。 为什么会有GIL由于物理上得限制,各CPU厂商在核心频率上的比赛已经被多核所取代。 为了更有效的利用多核处理器的性能,就出现了多线程的编程方式,而随之带来的就是线程间数据一致性和状态同步的困难。 即使在CPU内部的Cache也不例外 ,为了有效解决多份缓存之间的数据同步时各厂商花费了不少心思,也不可避免的带来了一定的性能损失。 Python当然也逃不开,为了利用多核,Python开始支持多线程。 而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。 于是有了GIL这把超级大锁,而当越来越多的代码库开发者接受了这种设定后,他们开始大量依赖这种特性(即默认python内部对象是thread-safe的,无需在实现时考虑额外的内存锁和同步操作)。 慢慢的这种实现方式被发现是蛋疼且低效的。 但当大家试图去拆分和去除GIL的时候,发现大量库代码开发者已经重度依赖GIL而非常难以去除了。 有多难?做个类比,像MySQL这样的“小项目”为了把Buffer Pool Mutex这把大锁拆分成各个小锁也花了从5.5到5.6再到5.7多个大版为期近5年的时间,本且仍在继续。 MySQL这个背后有公司支持且有固定开发团队的产品走的如此艰难,那又更何况Python这样核心开发和代码贡献者高度社区化的团队呢?所以简单的说GIL的存在更多的是历史原因。 如果推到重来,多线程的问题依然还是要面对,但是至少会比目前GIL这种方式会更优雅。 GIL的影响从上文的介绍和官方的定义来看,GIL无疑就是一把全局排他锁。 毫无疑问全局锁的存在会对多线程的效率有不小影响。 甚至就几乎等于Python是个单线程的程序。 那么读者就会说了,全局锁只要释放的勤快效率也不会差啊。 只要在进行耗时的IO操作的时候,能释放GIL,这样也还是可以提升运行效率的嘛。 或者说再差也不会比单线程的效率差吧。 理论上是这样,而实际上呢?Python比你想的更糟。 下面我们就对比下Python在多线程和单线程下得效率对比。 测试方法很简单,一个循环1亿次的计数器函数。 一个通过单线程执行两次,一个多线程执行。 最后比较执行总时间。 测试环境为双核的Mac pro。 注:为了减少线程库本身性能损耗对测试结果带来的影响,这里单线程的代码同样使用了线程。 只是顺序的执行两次,模拟单线程。 顺序执行的单线程(single_)#! /usr/bin/pythonfrom threading import Threadimport timedef my_counter():i = 0for _ in range():i = i + 1return Truedef main():thread_array = {}start_time = ()for tid in range(2):t = Thread(target=my_counter)()thread_array[tid] = tfor i in range(2):thread_array[i]()end_time = ()print(Total time: {}(end_time - start_time))if __name__ == __main__:main()同时执行的两个并发线程(multi_)#! /usr/bin/pythonfrom threading import Threadimport timedef my_counter():i = 0for _ in range():i = i + 1return Truedef main():thread_array = {}start_time = ()for tid in range(2):t = Thread(target=my_counter)()thread_array[tid] = tfor i in range(2):thread_array[i]()end_time = ()print(Total time: {}(end_time - start_time))if __name__ == __main__:main()

C++多线程编程问题

多线程工作可以提高解决问题的速度你举得例子可以多线程实现,但是更多的人称之为分布式计算多线程主要是创建多个线程,每个线程负责自己的事情,每个线程只对主线程负责至于解决问题的速度应该可以达到提高一半,但是你要注意资源的互斥,互斥处理不好估计会事倍功半

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: C

“揭秘性能与并发的机密-C-多线程编程 (性能与什么有关)” 的相关文章

编程实践-哈希负载均衡算法-IP-C (编程实践心得体会)

编程实践-哈希负载均衡算法-IP-C (编程实践心得体会)

Nginx 是一个高性能的开源 Web 服务器和反向代理服务器,因其高效处理高并发的请求而闻名。以下是一些 Nginx 用于处理高并发的主要特点和技术: 事件驱动架构: Nginx 使...

了解圈套和无效经常使用技巧-言语变长参数-C (了解圈套和无线的区别)

了解圈套和无效经常使用技巧-言语变长参数-C (了解圈套和无线的区别)

C工具 变长参数列表 这局部解释了旧的C格调变长参数列表。了解这些内容很关键,由于你或许会在遗留代码中遇到它们。但是,在新代码中,你应该经常使用变参模板来成功类型安保的变长参数列表。...

的区别-中-C-math.h-和-cmath (的区别中的钱)

的区别-中-C-math.h-和-cmath (的区别中的钱)

一、引言 C++规范库中的<cmath>和C言语规范库中的<math.h>均为数学函数库,它们提供了一系列数学函数和常量。但是,这两者之间存在一些关键的区别...

监禁程序后劲的优化利器-C-中的内联函数 (监禁等于坐牢吗)

监禁程序后劲的优化利器-C-中的内联函数 (监禁等于坐牢吗)

一、内联函数的定义和特点 内联函数是一种不凡的函数,它经过在编译时将函数调用交流为函数体中的代码,以缩小函数调用的开支,从而提高程序的口头效率。内联函数通罕用于那些函数体较小、调用频繁的场景...

与设计形式-可裁减的代码-C-打造可保养 (设计与形式的关系)

与设计形式-可裁减的代码-C-打造可保养 (设计与形式的关系)

引言 C++是一种弱小的编程言语,它提供了许多工具和库以支持面向对象编程和通用编程。设计形式是一种用于处置特定疑问的经过验证的处置打算。C++的灵敏性和设计形式的广泛...

提升性能和效率的全面指南-C-代码优化攻略 (提升性能和效率的英文)

提升性能和效率的全面指南-C-代码优化攻略 (提升性能和效率的英文)

对象池模式是一种设计模式,用于管理特定类型的对象。它在构造函数中预先分配了一定数量的对象,并在需要时从中获取对象,使用完毕后再将对象归还给对象池。这样可以减少频繁的动态内存分配和释放,提高性能。...

原理-成功链表-代码与解析-C (成功定律是什么)

原理-成功链表-代码与解析-C (成功定律是什么)

链表是一种经常出现的数据结构,它由一系列节点组成,每个节点蕴含数据和指向下一个节点的指针。与数组不同,链表不是延续的内存空间,而是经过指针链接在一同。上方咱们将深化讨论如何经常使用C++成功链表,...