其实Python多线程还有一个很重要的话题叫做GIL(GlobalInterpreterLock,即全局解释器锁)。多线程不一定比单线程快在Python中,可以通过多处理、多线程和多协程来实现多任务。多线程一定比单线程快吗?下面我用一段代码来证明我自己的观点。'''@作者:Runsen@微信公众号:Python之王@blog:https://blog.csdn.net/weixin_44510615@Date:2020/6/4'''importthreading,timedefmy_counter():i=0for_inrange(100000000):i=i+1returnTruedefmain1():start_time=time.time()fortidinrange(2):t=threading.Thread(target=my_counter)t.start()t.join()#第一次循环时join方法导致主线程阻塞,但是第二个线程没有启动,所以两个线程依次执行print("单线程顺序执行total_time:{}".format(time.time()-start_time))defmain2():thread_ary={}start_time=time.time()fortidinrange(2):t=threading.Thread(target=my_counter)t.start()thread_ary[tid]=tforiinrange(2):thread_ary[i].join()#两个线程都启动了,所以两个线程是并发的()运行结果单线程顺序执行total_time:17.754502773284912多线程执行total_time:20.01178550720215怕你说我搞砸了,还是截图看清楚这时候我怀疑:是不是我机器有问题?其实不是这样的。本质上Python线程是无效的,起不到并行计算的作用。Python线程确实封装了底层的操作系统线程,在Linux系统中是Pthread(全称POSIXThread),在Windows系统中是WindowsThread。另外,Python线程也完全由操作系统来管理,比如协调何时执行、管理内存资源、管理中断等等。GIL不是Python的特性。GIL的概念可以用一句话来解释,就是“在任何时候,无论有多少个线程,一个CPython解释器都只能执行一个字节码”。这个定义需要注意的地方:首先需要明确的是“GIL不是Python的特性”,它是在实现Python解析器(CPython)时引入的一个概念。C++是一套语言(语法)标准,但可以用不同的编译器编译成可执行代码。知名编译器如GCC、INTELC++、VisualC++等。Python也是如此。同一段代码可以通过CPython、PyPy、Psyco等不同的Python执行环境执行。“其他Python解释器不一定有GIL”。例如Jython(JVM)和IronPython(CLR)没有GIL,而CPython和PyPy有GIL;因为CPython是大多数环境中默认的Python执行环境。因此,在很多人的观念里,CPython就是Python,理所当然地把GIL归咎于Python语言的缺陷。所以这里要明确一点:“GIL不是Python的特性,Python可以完全独立于GIL。”GIL本质上是一个互斥锁。GIL本质上是一个互斥锁。既然是互斥锁,所有的互斥锁本质都是一样的,都是把并发操作变成串行操作,从而控制共享数据只能被一个任务同时修改,保证数据安全.有一点是可以肯定的:为了保护不同数据的安全,应该加不同的锁。GIL的工作原理:例如下图是GIL在Python程序中工作的例子。其中线程1、2、3依次执行。当每个线程开始执行时,它会锁定GIL以防止其他线程执行;同样,每个线程执行一段时间后,都会释放GIL让其他线程执行。线程开始使用资源。计算密集型计算密集型任务的特点是计算量大,消耗CPU资源。先看一个简单的计算密集型例子:'''@作者:润森@微信公众号:Python之王@blog:https://blog.csdn.net/weixin_44510615@Date:2020/6/4'''importtimeCOUNT=50_000_000defcount_down():globalCOUNTwhileCOUNT>0:COUNT-=1s=time.perf_counter()count_down()c=time.perf_counter()-sprint('timetakeninseconds->:',c)timetakeninseconds->:9.2957003这个是单线程,时间是9s,我们用两个线程看看结果:'''@作者:Runsen@微信公众号:Python之王@blog:https://blog.csdn.net/weixin_44510615@日期:2020/6/4'''importtimefromthreadingimportThreadCOUNT=50_000_000defcount_down():globalCOUNTwhileCOUNT>0:COUNT-=1s=time.perf_counter()t1=Thread(target=count_down)t2=Thread(target=count_down)t1.start()t2.start()t1.join()t2.join()c=time.perf_counter()-sprint('timetakeninseconds->:',c)timetakeninseconds->:17.110625我们程序的主要操作是计算,CPU不等待,但改成多线程后,加入线程后,频繁切换threads增加了时间开销,Time当然会增加。另一种是IO密集型。涉及网络和磁盘IO的任务都是IO密集型任务。这类任务的特点是CPU消耗低,任务大部分时间都在等待IO操作完成(因为IO的速度远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但有一个限度。最常见的任务是IO密集型任务,例如Web应用程序。》总结:对于io密集型工作(Python爬虫),多线程可以大大提高代码效率。对于CPU密集型任务(Python数据分析、机器学习、深度学习),多线程的效率可能略低于单线程。因此,在数据领域,没有多线程来提高效率,只有CPUtoGPU和TPU来提高计算能力。”
