Python语言的标准实现叫作CPython,它分两步来运行Python程序

  • 步骤1 :解析源代码文本,并将其编译成 字节码 (bytecode)
    • 字节码是一种底层代码,可以把程序表示成8位的指令
    • 从Python 3.6开始,这种底层代码实际上已经变成16位了
  • 步骤2 :CPython采用 基于栈的解释器来运行字节码
    • 字节码解释器在执行Python程序的过程中, 必须确保 相关的状态不受干扰,
    • CPython会用一种叫作全局解释器锁(global interpreter lock,GIL)的机制来实现运行的python程序的相关状态不受干扰
  • GIL实际上就是一种互斥锁(mutual-exclusion lock,mutex),用来防止CPython的状态在抢占式的多线程环境(preemptive multithreading)之中受到干扰,因为在这种环境下,一条线程有可能突然打断另一条线程抢占程序的控制权。如果这种抢占行为来得不是时候,那么解释器的状态(例如为垃圾回收工作而设立的引用计数等)就会遭到破坏。
  • CPython要通过GIL阻止这样的动作,以确保它自身以及它的那些C扩展模块能够正确地执行每一条字节码指令。
  • GIL会产生一个很不好的影响。在C++与Java这样的语言里面,如果程序之中有多个线程能够分头执行任务,那么就可以把CPU的各个核心充分地利用起来。尽管Python也支持多线程,但这些线程受GIL约束,所以每次或许只能有一条线程向前推进,而无法实现多头并进。
  • 所以,想通过多线程做并行计算或是给程序提速的开发者,恐怕要失望了。
    • 并发 concurrency : 指计算机似乎能在同一时刻做许多不同的事情
    • 并行 parallelism : 指计算机确实能够在同一时刻做许多不同的事情

多线程下的线程执行

  1. 获取GIL
  2. 执行代码直到sleep或者是 python虚拟机将其挂起。
  3. 释放 GIL

多线程效率低于单线程原因

  • 如上我们可以知道,在 python中想要某个线程要执行必须先拿到 GIL这把锁,且 python只有一个 GIL,拿到这个 GIL才能进入 CPU执行, 在遇到 I/O操作时会释放这把锁。如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100次操作就释放这把锁,让别的线程有机会 执行(这个次数可以通sys.setcheckinterval来调整)。所以虽然 CPython 的线程库直接封装操作系统的原生线程,但 CPython 进程做为一个整体,同一时间只会有一个获得了 GIL 的线程在跑,其它的线程都处于等待状态等着 GIL 的释放。
  • 而每次释放 GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于 GIL锁存在,python里一个进程永远只能同时执行一个线程 (拿到 GIL的线程才能执行 ),这就是为什么在多核 CPU上, python的多线程效率并不高

多线程效率低于或高于单线程原因

  • 相同的代码,为何有时候多线程会比单线程慢,有时又会比单线程快? 这主要跟运行的代码有关:
  • CPU密集型代码(各种循环处理、计数等等 ),在这种情况下,由于计算工作多, ticks计数很快就会达到 100阈值,然后触发 GIL的释放与再竞争 (多个线程来回切换当然是需要消耗资源的),所以 python下的多线程遇到 CPU密集型代码时,单线程比多线程效率高。
  • IO密集型代码 (文件处理、网络爬虫等 ),多线程能够有效提升效率单线程下有 IO操作会进行 IO等待,造成不必要的时间浪费。开启多线程能在线程 A等待时,自动切换到线程 B,可以不浪费 CPU的资源,从而能提升程序执行效率 。进行IO密集型的时候可以进行分时切换 所有这个时候多线程快过单线程

如果 python想充分利用多核 CPU,可以采用多进程

  • 每个进程有各自独立的 GIL,互不干扰,这样就可以真正意义上的并行执行。
    在 python中,多进程的执行效率优于多线程 (仅仅针对多核 CPU而言 )。所以在多核 CPU下,想做并行提升效率,比较通用的方法是使用多进程,能够有效提高执行效率

代码示例:

# 多线程
# 最后完成的线程的耗时
# [TIME MEASURE] execute function: gene_1000_field took 3840.604ms
@time_measure
def mult_thread(rows):
    # 总行数
    rows = rows
    # 线程数
    batch_size = 4
    cell = math.ceil(rows / batch_size)
    # 处理数据生成
    print('数据生成中,线程数:' + str(batch_size))
    threads = []
    for i in range(batch_size):
        starts = i * cell
        ends = (i + 1) * cell
        file = f"my_data_{str(i)}.csv"
        # t = threading.Thread(target=gene_1000_field_test, args=(starts, ends, file))
        t = threading.Thread(target=gene_1000_field, args=(starts, ends, file))
        t.start()
        threads.append(t)
    # for t in threads:
    #     t.join()
# 多进程
# [TIME MEASURE] execute function: gene_1000_field took 1094.776ms
# 执行时间和单个线程的执行时间差不多,目的达到
@time_measure
def mult_process(rows):
    # 总行数
    rows = rows
    # 线程数
    batch_size = 4
    cell = math.ceil(rows / batch_size)
    # 处理数据生成
    print('数据生成中,线程数:' + str(batch_size))
    process = []
    for i in range(batch_size):
        starts = i * cell
        ends = (i + 1) * cell
        file = f"my_data_{str(i)}.csv"
        # p = Process(target=f, args=('bob',))
        # p.start()
        # p_lst.append(p)
        # t = threading.Thread(target=gene_1000_field_test, args=(starts, ends, file))
        p = Process(target=gene_1000_field, args=(starts, ends, file))
        p.start()
        process.append(p)

参考文章:

  1. https://www.cnblogs.com/caopeiyang/p/9418897.html
  2. https://www.cnblogs.com/nickchen121/p/11130256.html
python多线程比单线程效率低的原因是:GILpython中有一个 GIL( Global Interpreter Lock),中文为:全局解释器锁 - 最开始时候设计GIL是为了数据安全。python为了数据安全设计了这个 GIL - 每个 CPU在同一时间只能执行一个线程 - 在单核 CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。 但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生) 在
单线程实现多个定时器 NewTimer.py复制代码 代码如下:#!/usr/bin/env python from heapq import *from threading import Timerimport threadingimport uuidimport timeimport datetimeimport sysimport math global TimerStampglobal TimerTimesclass CancelFail(Exception):    pass class Slot(object):    def __init__(self, period=0, in
python的多进程性能要明显优于多线程,因为cpython的GIL对性能做了约束。 Python是运行在解释器中的语言,查找资料知道,python中有一个全局锁(GIL),在使用多进程(Thread)的情况下,不能发挥多核的优势。而使用多进程(Multiprocess),则可以发挥多核的优势真正地提高效率。 资料显示,如果多线程的进程是CPU密集型的,那多线程并不能有多少效率上的提升,相反还可能会因为线程的频繁切换,导致效率下降,推荐使用多进程;如果是IO密集型,多线程进程可以利用IO阻塞等待时的空闲时间执行其他线程,提升效率。所以我们根据实验对比不同场景的效率
Python必看:为什么Python 多线程效率不升反降? 在Python学习或项目开发过程中,许多小伙伴反应说Python 多线程是鸡肋,效率不升反降。难道多线程不好吗?在我们的常识中,多线程通过并发模式充分利用硬件资源,大大提升了程序的运行效率,怎么在 Python 中反而成了鸡肋呢? Python中的多线程是不是鸡肋,我们先做个实验,实验非常简单,就是将数字 “1亿” 递减,减到 0 程序...
很多时候,当我们需要使用Python来处理大量的数据的时候,为了缩短处理的时间,我们会使用多线程或多进程来并行处理任务。 由于Python全局解释器锁的存在,导致在执行多线程的时候实际上只有一个线程在运行,这使得多核CPU无法发挥它真正的效率。而多进程就可以很好的解决这个问题。如果你打开多进程的姿势不对,会导致它比单进程更慢,下面我们就来看看如何正确的打开多进程。 系统:Ubuntu16.04 Python:3.7 这个示例是基于Python对图片做一个预处理,以便于后面神经网络使
在使用多线程时,一定要知道一个道理:处理速度的最终决定因素是CPU、内存等,在单CPU(无论多少核)上,分配CPU资源的单位是“进程”而不是“线程”。 我们可以做一个简单的试验: 假设我要拷贝100万条数据,单CPU电脑,用一个进程,在单线程的情况下,CPU占用率为5%,耗时1000秒。那么当在这个进程下,开辟10个线程同时去运行,是不是CPU占用...
2014-05-04 07:56:50cnblogs.com-Ethan Cai-点击数:306 “多个人干活比一个人干活要快,多线程并行执行也比单线程要快”这是我学习编程长期以来的想法。然而在实际的开发过程中,并不是所有情况下都是这样。先看看下面的程序(点击下载): ThreadTester是所有Tester的基类。所有的Tester都干的是同样一件事情,把counter增加到100000...
def music(func, loop): for i in range(loop): print("I was listening to %s the %d time! %s" % (func, i+1, time.time())) time.sleep(2) # 视频播放器 def movie(func, loop
首先分配cpu资源的单位是进程。一个进程分配的cpu资源是一定的。 程序在执行的过程中消耗的是cpu,比如只有一个单核cpu,多个线程同时执行工作时,需要不断切换执行,这就是线程的上下文切换,时间耗费更多,而单线程只是一个线程再跑。 多线程提高的是并发数量,执行的是不变的,比如现在有一个4核cpu的服务器,同一时间可执行4个线程,这样处理线程任务 的速度比较快。但是多出来的线程,5个,6个,7个,...
多进程----2.1亿次计算: 3.185178518295288 多进程----1800万次IO: 1.8847143650054932 10个线程----2.1亿次计算:9.001402378082275 10个线程----1800万次IO: 6.638666391372681 进程----2.1亿次计算 single cup: 7.320388317108154 进程----1800...
一、功能说明 最近进行一个功能优化工作,涉及到数据的传输,由于散乱的小文件太多的缘故,因此在数据传输过程中非常慢。计划通过对文件夹进行压缩后传输二进制压缩包的方式提升网络传输效率。前期需要对某个目录下的所有文件夹逐个压缩为zip包,此处引入多线程处理和单线程处理,对比一下压缩速度。 代码并未提供全部,主要是为了记录多线程处理文件时参数分配以及进度界面显示问题。 二、功能界面 三、主要功能代码 // MutiThreadDlg.h : 头文件 #pragma once #include "af
在某些情况下,使用Python多线程下载可能比使用单线程下载慢,主要是由于以下几个原因: 1. 全局解释器锁(GIL):Python的全局解释器锁是为了确保在同一时间只有一个线程执行Python字节码。这意味着在多线程情况下,多个线程无法同时执行Python代码,从而降多线程下载的效率。 2. I/O密集型任务:多线程在处理I/O密集型任务,如网络请求和文件操作时,由于GIL的存在,多线程下载并不会真正并行处理这些任务,导致效率并不提升。 3. 线程开销:多线程在切换线程和共享数据等操作时会引入额外的开销。线程的创建、销毁和切换需要消耗时间和资源,而频繁的切换线程也会增加额外开销,导致下载速度变慢。 4. 限制带宽:下载速度受到网络带宽和服务器响应速度的限制。即使使用多线程下载,服务器可能仍然限制每个连接的速度,因此多线程并不能提升整体下载速度。 综上所述,尽管多线程可以在某些情况下提高程序的并发性和执行效率,但由于Python的GIL和线程开销等原因多线程下载在某些情况下可能比单线程下载速度慢。如果要提高下载速度,可以考虑使用其他语言编写多线程下载程序,或者采用其他并发模型,如多进程或异步IO。