Cython屏蔽GIL锁实践
环境:
Ubuntu19.10
四核八线程.
题外话,八线程属于超线程概念,程序员不可控制,属于操作系统调度的工作.
基本概念:
什么时候使用并发/并行?
资料中有两种说法:
一种是:
涉及到多核就是并行,单核IO异步就是并发
另一种是:
整个系统服务于IO异步任务就是并发,涉及到CPU密集就是并行
本文以第一种为准
--------------------------------
消除GIL锁为什么会导致单线程变慢?
参考[6]的回答
Python多线程相当于单核多线程[7]
--------------------------------
网上资料的多线程包含:
单核多线程和多核多线程.
单核多线程就IO异步.
多核多线程大多数情况下指的是多核情况.
线程和并发有关系,进程和隔离有关系
根据[4]
I'd welcome a set of patches into Py3k only if the performance for a single-threaded program (and for a multi-threaded but I/O-bound program) does not decrease”(这个意思是如果移除GIL锁,那么单线程性能会下降,也就是I/O异步性能会下降)
Each Python process gets its own Python interpreter and memory space so the GIL won't be a problem. (每个进程都有一个GIL锁)
网上说的"Python的多线程是鸡肋"的意思指的是多核多线程
有了多线程执行异步为啥还要多协程执行IO异步?
根据[5]:
好处是协程的开销成本更低.
GIL锁影响的是IO密集还是CPU密集任务?
The GIL does not have much impact on the performance of I/O-bound multi-threaded programs as the lock is shared between threads while they are waiting for I/O.[4](影响的是CPU密集任务而不是IO密集任务)
GIL锁在你调用开源库的时候,依然存在吗?
取决于底层是不是C写的,如果是C写的,屏蔽了GIL锁,那就不存在,否则就存在
----------------------------------------------------------------------------------------------------------------------
网上关于屏蔽GIL锁的资料如下:
[1]屏蔽了GIL锁,没有完整代码
[3]讲了原理,但是没有实操
本篇博客重点解析[2].
GIL锁每个进程都有一个,锁的到底是什么?
锁的是多线程进行CPU密集计算时候的场景.案例如下[4]:
单线程:
# single_threaded.py
import timeCOUNT = 50000000def countdown(n):while n>0:n -= 1start = time.time()
countdown(COUNT)
end = time.time()print('Time taken in seconds -', end - start)
Time taken in seconds - 2.2484254837036133
多线程:
# multi_threaded.py
import time
from threading import ThreadCOUNT = 50000000def countdown(n):while n>0:n -= 1t1 = Thread(target=countdown, args=(COUNT//2,))
t2 = Thread(target=countdown, args=(COUNT//2,))start = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
end = time.time()print('Time taken in seconds -', end - start)
Time taken in seconds - 3.074549674987793
多进程:
from multiprocessing import Pool
import timeCOUNT = 50000000
def countdown(n):while n>0:n -= 1if __name__ == '__main__':pool = Pool(processes=2)start = time.time()r1 = pool.apply_async(countdown, [COUNT//2])r2 = pool.apply_async(countdown, [COUNT//2])pool.close()pool.join()end = time.time()print('Time taken in seconds -', end - start)
结论:
CPU密集任务速度上,
多核多进程>多核多线程>单线程
----------------------------------------------下面是屏蔽GIL锁实践------------------------------------------------------------------------------
回到前面一个话题,python的多线程真的是鸡肋吗?
并不是,请看下面实验,下面实验受到[2]的启发,但是因为[2]的代码不完整,所以我自己想出了一个demo
我们把count提升100倍,下面是三个代码文件:
pycall.c
#include <stdio.h>
#include <stdlib.h>
// #include <iostream.h>
int foo(int a, int b)
{ long long int count=5000000000;while( count > 0 ){// cout << "a 的值:" << a << endl;count--;if(count==2500000000)printf("计数中%lld\n", count);if(count==0)printf("计数结束%lld\n", count);};printf("you input %d and %d\n", a, b); return 1;
}
因为我们等下要进行多线程测试,
所以我们在上面代码中故意加入一些if语句用来监控计数过程,如果多线程生效 那么就会打印两次,
如果GIL锁依然存在,那么整个运行过程会比单线程还要慢一倍左右
pycall-1thread.py
import ctypes
import threading
import timeif __name__ == '__main__':start=time.time()my_lib=ctypes.cdll.LoadLibrary("./libpycall.so") t1=threading.Thread(target=my_lib.foo,args=(1,1))t1.start()t1.join()end=time.time()print('耗时=',end-start)
pycall-2thread.py
import ctypes
import threading
import timeif __name__ == '__main__':start=time.time()my_lib=ctypes.cdll.LoadLibrary("./libpycall.so") t1=threading.Thread(target=my_lib.foo,args=(1,1))t2=threading.Thread(target=my_lib.foo,args=(2,2))t1.start()t2.start()t1.join()t2.join()end=time.time()print('耗时=',end-start)
运行方法:
先生成.so文件
gcc -o libpycall.so -shared -fPIC pycall.c
然后:
命令 | 运行结果 |
python pycall-1thread.py |
计数中2500000000 计数结束0 you input 1 and 1 耗时= 10.828569173812866 |
python pycall-2thread.py |
计数中2500000000 计数中2500000000 计数结束0 you input 1 and 1 计数结束0 you input 2 and 2 耗时= 10.749252796173096 |
可以看到,双线程运行与单线程运行速度一致,真正发挥了多核的威力.
所以明白如何使用python的多线程吗?
在关键部位使用C/C++代码来撰写,屏蔽GIL锁,大大提升计算速度.
C/C++写起来如果觉得繁琐,可以参考opencv,很多可以直接拿来用喔
Reference:
[1]Python解决GIL锁的办法
[2]python的GIL锁优化方案及性能对比
[3]GIL锁
[4]What is the Python Global Interpreter Lock (GIL)?
[5]线程和进程的区别是什么? - 无与童比的回答 - 知乎
[6]python中去掉gil会大幅降低单线程的执行速度,怎么理解?
[7]为什么有人说 Python 的多线程是鸡肋呢? - DarrenChan陈驰的回答 - 知乎
[8]Python 的GIL与线程锁
[9]Python 中的 GIL 如果可以去掉的话,是不是只要有线程锁就完全不会产生负面影响?
[10]对于多线程程序,单核cpu与多核cpu是怎么工作的
[11]单核多线程与多核多线程的区别---总结
[12]【什么时候用多线程——CPU篇】
[13]协程池
[14]多线程有什么用?
[15]How does a single CPU handle Multi-threaded and multi-process applications?
Cython屏蔽GIL锁实践相关推荐
- Python GIL锁问题探究及解决
1. 什么是GIL? GIL即全局解释器(global interpreter lock).python的每个线程在执行时都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有持有G ...
- python中gil锁和线程锁_Python线程——GIL锁、线程锁(互斥锁)、递归锁(RLock)...
GIL锁 计算机有4核,代表着同一时间,可以干4个任务.如果单核cpu的话,我启动10个线程,我看上去也是并发的,因为是执行了上下文的切换,让看上去是并发的.但是单核永远肯定时串行的,它肯定是串行 ...
- Python 线程----线程方法,线程事件,线程队列,线程池,GIL锁,协程,Greenlet
主要内容: 线程的一些其他方法 线程事件 线程队列 线程池 GIL锁 协程 Greenlet Gevent 一. 线程(threading)的一些其他方法 from threading import ...
- 并发编程中的GIL锁(全局解释器锁)自己理解的他为啥存在
自己的分析 GIL锁就是一个全局解释器锁 也就是python中因为有垃圾回收机制的存在.垃圾回收机制也是一个线程,如果所有的线程都可以使用cpu的不同资源(也就是多核 cpu并行处理线程的情况) -这 ...
- 一句话讲清楚GIL锁
什么是"全局GIL锁" 全局指的是所有CPU, 锁指的每次是只有一个CPU的线程可以执行IO任务 银行四个窗口(进程),四个队伍的客户在排队办理业务, python中的" ...
- 递归锁、信号量、GIL锁、基于多线程的socket通信和进程池线程池
递归锁.信号量.GIL锁.基于多线程的socket通信和进程池线程池 递归锁 死锁现象:是指两个或两个以上的进程和线程因抢夺计算机资源而产生的一种互相等待的现象 from threading impo ...
- python gil锁存在的意义_关于python的GIL全局解释器锁的简单理解
GIL是解释器内部的一把锁,确切一点说是CPython解释器内部的一把锁,所以要注意区分 这和我们在Python代码中使用线程锁Lock并不是一个层面的概念. 1. GIL产生的背景: 在CPytho ...
- python 对象锁_也许你对 Python GIL 锁的理解是 错的。
摄影:产品经理甜白与草莓更配~ 我刚到现在这个公司时,听到当时一个高级工程师(现已离职)大声地跟他旁边的同事说: Python 有 GIL 锁,所以它的多线程实际上是单线程,所以写多线程代码不用考 ...
- Python GIL 锁 - Python零基础入门教程
目录 一.并行和并发 二.GIL 锁 案例一:单核多线程 案例二:单核多线程 案例三:双核多线程 三.如何解决 GIL 锁问题 四.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习 ...
最新文章
- linux 内核按键抖动,Tiny4412 Linux驱动之按键(定时器防抖动) | 技术部落
- js里父页面与子页面的相互调用
- 布局管理器 2----- 表格布局
- 盘点JavaScript函数的基本知识
- Android Studio修改debug.keystore
- 嘉益仕(Litns)带您读懂MES系统:选型篇
- 才博教育:AI口语学习平台上云
- 《机器学习训练秘籍》中文版58章节 完整开源 吴恩达
- rssi室内定位算法原理_室内定位方案常用的4种定位算法
- emif接口速率问题_OMAPL138 EMIF读取速率问题
- python tkinter button_Python连载60-Tkinter布局、按钮以及属性详解
- Installation of Requests
- 三层架构和mvc的区别_谈谈对于企业级系统架构的理解
- Anylogic学习--------------数学函数
- python 欠采样_Python sklearn 实现过采样和欠采样
- 信号完整性(SI)电源完整性(PI)学习笔记(十五)有损线、上升边退化与材料特性(二)
- java等待所有子线程执行完毕再执行
- 盘点2019年经典营销案例
- android 读取 build.prop,读取 android /system/build.prop 的最简单方法
- java使用pinyin4j实现汉字到拼音转换
热门文章
- [转载]We Recommend a Singular Value Decomposition
- Ubuntu switch window switch terminal tab
- 模板类的全特化、偏特化
- Ajax-基础篇(02)
- The 4+1 view model
- CGCTF-Web-签到题
- Python3爬虫之中文乱码问题分析与解决方法
- 如何给multicraft装PHP,我的世界Linux搭建Multicraft网页后台教程更新和添加服务端文件...
- cad快速看图需要替换的4个vip文件_看不懂施工图怎么办?20年老师傅教你看图技巧,学会受用一生...
- 提交git push 的时候报错,Please make sure you have the correct access rights