ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python多线程比单线程效率低的原因及其解决办法

2021-06-10 21:32:16  阅读:386  来源: 互联网

标签:多线程 python 单线程 线程 GIL CPU


python多线程比单线程效率低的原因是:GIL

python中有一个 GIL( Global Interpreter Lock),中文为:全局解释器锁

 - 最开始时候设计GIL是为了数据安全。python为了数据安全设计了这个 GIL
 - 每个 CPU在同一时间只能执行一个线程
 -  在单核 CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。 但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生)
 在 python多线程下,每个线程的执行方式如下:

在 python多线程下,每个线程的执行方式如下:

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

为什么有时候多线程效率低于单线程?

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

为什么python的多线程利用不了多核cpu,那为什么比单线程速度要慢?

同的代码,为何有时候多线程会比单线程慢,有时又会比单线程快? 这主要跟运行的代码有关: 
1、CPU密集型代码(各种循环处理、计数等等 ),在这种情况下,由于计算工作多, ticks计数很快就会达到 100阈值,然后触发 GIL的释放与再竞争 (多个线程来回切换当然是需要消耗资源的),所以 python下的多线程遇到 CPU密集型代码时,单线程比多线程效率高。 
2、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,单线程,线程,GIL,CPU
来源: https://blog.csdn.net/weixin_42176112/article/details/117790945

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有