python多线程与_thread模块
进程与线程
1.进程:计算机程序只是存储在磁盘中的可执行二进制(或其他类型)的文件。只有把他们加载到内存中并被操作系统调用,才具有其生命周期。进程则是一个执行中的程序。每个进程都拥有自己的地址空间,内存,数据栈以及其他用于跟踪执行的辅助数据。进程也可以通过派生新的进程来执行其他任务。由于每个进程有自己的数据,所以只能采用进程间通信(IPC)的方式来共享信息。
2.线程:又称轻量级进程。一个进程开始便会创建一个线程,称为主线程。一个进程可以创建多个线程,多线程即是同一进程下的不同执行路径,同一进程下的线程共享该进程的数据区。线程以并发的方式执行,线程执行时可以被中断和挂起。(在多核cpu中,多线程才可能并行执行)
图解多线程与单线程:单核与多核
线程和python
1.全局解释器锁
python代码执行是由python虚拟机(又称解释器主循环)控制的。python在主循环中同时只能有一个控制线程在执行,就像单核cpu系统中,内存中可以有很多程序,但任意给定时刻只能有一个程序在运行。同理,尽管python解释器可以运行多线程,但在任意给定时刻只有一个线程会被解释器执行。
对python虚拟机由全局解释器锁(GIL)控制。正是由于GIL的存在,python解释器在某一时刻只能让一个线程执行。多线程执行方式如下:
1)设置GIL
2)切换一个线程去执行
3)执行下面操作之一
a.指定数量字节码指令
b.线程让出控制权
4)线程设置成睡眠(挂起)状态
5)解锁GIL
6)重复1-5
2.python多线程的作用,原理,缺陷
作用:提高程序执行速度。
原理:多线程能够提高执行速度的原因是什么?假如一个程序包含多个子任务,这些任务相互独立,没有因果关系。
a.单线程情况下,执行过程中,某个子任务在等待I/O,然而I/O到来的时间不确定,cpu时间耗在毫无意义的等待上,程序执行时间也将加上这一段等待的时间。
b.多线程情况下,若某个子任务等待I/O,可切换出其他线程执行,等到合适的时机(I/O到达)再切换回该线程,避免了cpu无意义的等待,也降低了程序的执行时间。
缺陷:由于GIL的存在,python多线程中只能有一个线程会被执行,因而无法利用多核cpu能够实现并行执行的特点。不仅如此,由于线程的切换需要时间开销,多线程使用不当的程序执行速度还可能要慢于单线程程序执行的速度。
3.python多线程的使用场合
I/O密集型应用
_thread模块
python的_thread模块提供了基本的线程和互斥锁支持,threading模块则提供了功能更全面的线程管理。以下讨论_thread模块
主要方法
_thread.start_new_thread(function,args,kwargs=None) //派生一个新的线程,给定agrs和kwargs来执行function
_thread.allocate_lock() //分配锁对象
_thread.exit() //线程退出
lock.acquire(waitflag=1, timeout=-1) //获取锁对象
lock.locked() //如果获取了锁对象返回true,否则返回false
lock.release() //释放锁
其他方法
_thread.LockType() //锁对象类型
_thread.get_ident() //获取线程标识符
-thread.TIMEOUT_MAX //lock.acquire的最大时间,超时将引发OverflowError
_thread.interrupt_main() //引发主线程KeyboardInterrupt错误,子线程可以用这个函数来终止主线程
简单实例
4个线程分别执行loop函数,中间等待nsec秒,nsec分别为4,2,3,5
1 #!/usr/bin/env python3 2 # coding:utf-8 3 from time import ctime 4 from time import sleep 5 import _thread 6 7 loops = [4, 2, 3, 5] 8 9 10 def loop(nloop, nsec, lock): # 参数依次为:标识,睡眠时间,锁对象 11 print("start loop", nloop, 'at:', ctime()) 12 sleep(nsec) 13 print("loop", nloop, "done at:", ctime()) 14 lock.release() # 释放锁 15 16 17 def main(): 18 print('start at:', ctime()) 19 locks = [] 20 nloops = range(len(loops)) 21 22 for i in nloops: 23 lock = _thread.allocate_lock() # 分配锁对象 24 lock.acquire() # 获取锁对象 25 locks.append(lock) 26 27 for i in nloops: 28 _thread.start_new(loop, (i, loops[i], locks[i])) //派生新线程 29 30 # 等待所有锁被释放 31 for i in nloops: 32 while(locks[i].locked()): 33 pass 34 print('all DONE at', ctime()) 35 36 37 if __name__ == '__main__': 38 main()
执行结果:
start at: Mon Jan 22 16:09:10 2018
start loop 2 at: Mon Jan 22 16:09:10 2018
start loop 1 at: Mon Jan 22 16:09:10 2018
start loop 3 at: Mon Jan 22 16:09:10 2018
start loop 0 at: Mon Jan 22 16:09:10 2018
loop 1 done at: Mon Jan 22 16:09:12 2018
loop 2 done at: Mon Jan 22 16:09:13 2018
loop 0 done at: Mon Jan 22 16:09:14 2018
loop 3 done at: Mon Jan 22 16:09:15 2018
all DONE at Mon Jan 22 16:09:15 2018
分别等待4,2,3,5秒,程序运行总时间5秒。main()函数最后一个循环作用是等待所有子线程退出。
注意事项
_thread对于进程何时退出没有任何控制。当主线程结束时,所有其他线程也都强制结束。不会发出警告或者进行适当的清理。因而python多线程一般使用较为高级的threading模块,它提供了完整的线程控制机制以及信号量机制。
可参考: