今天看到一篇文章, 讲述的是几个提升 python 性能的项目:
在看的过程中, 接触到一个名词, 一个从学 python 开始就一直看到, 但是从来都是一知半解的名词, 心里不开心, 必须把它搞明白, 对了, 这个词就是 GIL. 网上搜索了一些资料, 粗浅的理解了什么是 GIL, 自己感觉学习的过程比较好, 感觉略有收获, 老规矩, 为了巩固知识, 自己整片文章出来写一写, 其实好多文章已经写的很完善了, 所以这篇随笔, 只做知识巩固, 如有雷同, 请各位原创作者原谅, 小菜鸟一枚, 如果哪里写的有问题, 还请各位前辈不吝指正.
一句话: 解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁.
首先, GIL 的全名, Global Interpreter Lock, 鉴于英文水平, 不做名词翻译, 以免误导. 大体解释一下, 这个锁就是用来为了解决 Cpython 多线程中线程不安全问题引入的一个全局排它锁, 它的作用就是在多线程情况下, 保护共享资源, 为了不让多个线程同时操作共享资源, 导致不可预期的结果而加上的锁, 在一个线程操作共享资源时, 其他线程请求该资源, 只能等待 GIL 解锁. 这个设置在 Cpython 刚引入多线程概念的时候就有了, 然后后续的各种包和组件开发都不可避免的受到了 GIL 的影响, 所以有人会说, python 在多线程处理的时候很慢. python GIL 实现方式类似于如下伪代码:
if __name__ == '__main__':
GIL 锁开始运作
主线程做操作
主线程完成操作
GIL 锁释放资源
所以多线程共同操作共享资源的时候, 有一个线程竞得了资源, 它就被 GIL 锁保护起来, 其他线程只能是在那里等着, 但是这个时候, 线程的休眠唤醒, 全部会消耗 CPU 资源, 所以嘞, 就会慢.
看到这个时候, 我又发现了一个名词: 线程安全. 这个名词, 也是那种特别熟悉, 但就是无法清晰的说出它是啥的概念. 查了资料, 在这记一下:
线程安全就是多线程访问时, 采用了加锁机制, 当一个线程访问该类的某个数据时, 进行保护, 其他线程不能进行访问直到该线程读取完, 其他线程才可使用. 不会出现数据不一致或者数据污染. 线程不安全就是不提供数据访问保护, 有可能出现多个线程先后更改数据造成所得到的数据是脏数据.
我自己想了一下, 大约就是这样, 比如整个列表, 俩个线程同时在列表中 append 操作, 如果没有锁的保护, 在机缘巧合之下, 俩个线程同时先后申请了空间且没来得及插入数据, 然后这时列表中只会有一个空间, 那么在插入过程中只能有一个数据写入, 会造成不可知后果, 有可能报错终止, 有可能有一个线程操作没成功, 那么这个就是线程不安全了, 大白话说, 只要线程之间没有共享资源, 那么就是线程安全的, 有共享资源, 为了保证线程安全, 需要引进锁的机制.
而后的文章中, 有前辈做过实验:
顺序执行的单线程 (single_thread.py)
- #! /usr/bin/python
- from threading import Thread
- import time
- def my_counter():
- i = 0
- for _ in range(100000000):
- i = i + 1
- return True
- def main():
- thread_array = {}
- start_time = time.time()
- for tid in range(2):
- t = Thread(target=my_counter)
- t.start()
- t.join()
- end_time = time.time()
- print("Total time: {}".format(end_time - start_time))
- if __name__ == '__main__':
- main()
同时执行的两个并发线程 (multi_thread.py)
- #! /usr/bin/python
- from threading import Thread
- import time
- def my_counter():
- i = 0
- for _ in range(100000000):
- i = i + 1
- return True
- def main():
- thread_array = {}
- start_time = time.time()
- for tid in range(2):
- t = Thread(target=my_counter)
- t.start()
- thread_array[tid] = t
- for i in range(2):
- thread_array[i].join()
- end_time = time.time()
- print("Total time: {}".format(end_time - start_time))
- if __name__ == '__main__':
- main()
最终结果如下:
以上测试代码和图片引用自:
http://cenalulu.github.io/python/gil-in-python/
过程证明了因为 GIL 的存在, 导致 python 在使用多线程的时候反而不如顺序执行快.
此处我又温习了一下 python 线程:
线程的顺序执行还是多线程并发, 取决于 join 函数的位置. join 函数的作用是等待当前线程结束, 所以每一个线程创建之后, 调用 start 函数, 这是在后面跟上该线程的 join 函数, 那么就是顺序执行, 如果多个线程先完成创建和 start, 最后加上 join 函数, 那么就变成了多线程并发.
这就是今天的学习内容, 其实所有知识网上都能找到, 更想分享的是一种学习的方法, 一种本身很不推荐的学习方法, 那就是类似于探索性测试的学习, 啥不懂就去看啥, 有些时候, 我们学习东西确实不能非要究其内在, 软件行业的学习本身在非本行人事看来就特别神奇且枯燥, 所以最初的学习, 我们需要整个图形界面, 让我们学到的东西有了成就感, 如果上来先去研究机器码, 那么没几个人愿意学下去, 但是不管怎样, 既然走上了软件行业的道路, 这种探索性, 打破砂锅问到底的学习, 在我的感觉里应该是必经之路, 也就是所谓的底层研究. 以安卓开发举例, 如果做安卓开发的, 虽然能写出很漂亮的界面, 解决所有的 bug, 如果不了解安卓系统 Linux 层的知识, 在我的眼里, 从未把这种研发看做大牛. 当然我并不觉得不了解 Linux 底层的安卓研发可以解决任何 bug
当下的软件行业进入了一个神奇的阶段, 我已经听过无数遍的理论, 培训机构出来就能赚钱, 大学读着没用, 在这里不讨论教育体制问题, 从个人情感上, 我觉得大学教育虽然没有教给学生直接找工作的技能, 但是给了所有学生一个能够了解基础知识的园地, 换而言之, 作为行业的一员, 总应该有将行业发展起来的觉悟, 行业内整体风气, 缺乏静下心来的沉淀. 在大谈敏捷, 行为驱动, 机器学习的同时, 自己需要静下心来回头看看, 基础已然不牢, 再走下去是否有些危险. 是不是学习软件技术, 就是为了获取互联网行业那虚高的工资, 是否已经局限于第三方框架, 一旦框架出现问题, 只能打给客服而束手无策, 是否有过没有做任何尝试就将 bug 归咎于安卓系统, 阿里中间件等等, 是不是旧技术还没用明白, 为了新技术就可以不再去研究.
来源: http://www.bubuko.com/infodetail-2961204.html