该文章参考了http://www.liaoxuefeng.com/wi... 廖雪峰的教程。python
一个进程至少有一个线程。
Python也提供多线程支持,并且Python中的线程并不是是模拟出来的多线程,而是系统级别的Posix Thread.git
Python标准库提供了两个模块thread
和threading
。前者是低级库,后者是高级库。高级库是对低级库的封装。
一般状况下,咱们只须要使用threading
高级库就能够了。github
咱们只须要建立threading.Thread
类,并传入一个须要线程执行的函数做为target便可。编程
import threading import time def loop(): count = 1 while count < 4: print "Hello" time.sleep(1) count += 1 if __name__ == '__main__': thread = threading.Thread(target=loop, name="LoopThread") thread.start() thread.join() current_thread = threading.current_thread() print current_thread.getName() print "LoopThread Ended"
上述代码主进程默认会建立一个主线程。
上面的代码中,咱们用到了多个函数。多线程
join()
函数:即让主线程序等待子线程完成后继续。函数
current_thread()
函数:获取当前线程的实例。oop
同一个变量,进程会拷贝一份存于各个进程中,互不影响。
同一个变量,线程则会互相共享。这就会带来一些问题。由于cpu对线程是随意切换的。那么若是一个线程对某一个变量进行多步cpu操做的时候,若是不加锁,那么可能会在线程进行到一半的时候,会被切换到其余线程,从而使得操做变乱。
一个典型的例子是“一存一取”的场景。
例如多个线程对银行中的某一个帐户进行操做。一个线程须要对帐户进行一存一取某一个金额的操做才会让帐户平衡。
可是多个线程对该帐户进行操做的时候,且它们操做的金额常常是不同的,那么最终的帐户极可能是不平衡的。ui
thread 1: -100 thread 1: +100 thread 1: -100 thread 1: +100 thread 2: -200 thread 2: +200 thread 2: -200 thread 2: +200
上述描述了两个线程按顺序互补干扰地对某一个帐户进行操做。
可是实际状况是cpu随意切换线程。上述操做是打乱的,例如操作系统
thread 1: -100 thread 2: -200 thread 2: +200 ...
此时,咱们发现帐户并非平衡的。
若是解决呢?咱们须要对每个thread的一存一取操做加一把锁。即改帐户变量必须是让一个线程先进行完整的一存一取操做后,才能被其余线程所操做。.net
咱们能够将要上锁的代码包裹起来,即:
lock.acquire() num = num + n num = num - n print num lock.release()
固然,lock能够对资源的获取和释放,那么咱们也能够用with
关键字。
with lock: num = num + n num = num - n print num
死锁的发生每每是两个线程各自占用了对方所须要用到的资源而都在等待对方释放资源而形成的。
碰见死锁后,程序则会一直暂停在那里。直到系统将它们关闭。
说到Python的多线程编程,就会绕不过GIL。那么GIL又是什么鬼呢?
GIL听说是Python中hardest的问题,想要完全了解GIL,必需要对操做系统设计、多线程编程、C语言、解释器设计和CPython解释器的实现有着很是完全的理解。
据廖雪峰中的教程中描述:
Python中的线程虽然是真正的线程,可是解释器执行代码的时候,会有一个GIL锁(Global Interpreter Lock). 任何Python线程执行前,必须先得到GIL锁。而后,每执行100条字节码,解释器就会自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把全部线程的执行代码都给上了锁。所以,多线程在Python中只能交替进行,即便100个线程跑在100核CPU上,也只能用到1个核心。
GIL是Python解释器设计的历史遗留问题,一般咱们用的解释器是官方实现完全CPython。
更多关于GIL设计的历史渊源,能够阅读:
那么是否是Python就是不能利用多核处理器任务了呢?咱们以前学到过多进程编程,每个进程都有一个独立的GIL锁,各进程互不影响。