python程序解释依靠解释器主循环,主循环中同时只能有一个控制线程在运行,类似单核CPU,是伪并发,python保证只有一个线程运行的机制是使用全局解释器锁GIL,如下:
1.设置GIL
2.切换一个线程运行
3.执行指定数量的字节码指令或者线程yielding
4.设置线程为睡眠状态
5.解锁GIL
调用外部代码比如C或者C++的时候,GIL会保持锁定,因为此时没有python字节码指令,
python提供了一些模块用于支持多线程编程,有thread,threading,Queue等,一般不使用thread模块,因为缺少同步原语,主线程退出之后,其他线程都会在没有清理的情况下直接退出,需要使用很笨拙的方法实现等待所有子进程,且没有任何方法可以实现守护进程
thread模块组件:
start_new_thread(function, args, kwargs=None):核心函数,创建线程,第一个参数是函数或者对象,第二个参数是函数参数,必须使用元组,没有参数的话传入空元组
allocate_lock:分配LockType锁
exit():线程退出
LockType锁对象的方法:
acquire(wait=None):尝试获取锁
locked():获取锁对象,成功返回True,否则返回False
release():释放锁
thread模块实现同步的两种笨拙方法:
1.主线程等待子线程执行的最长时间
2.每个字线程获取一个锁,主线程循环检查所有锁都被释放
threading模块组件:
Thread:表示一个执行线程的对象
Lock:锁原语对象,和thread模块一样
RLock:可重入锁,单个线程可以再次获取已持有的锁,即递归锁
Condition:条件变量
Event:一定数量的线程等待某个事件发生,之后所有线程被激活
Semaphore:信号量
Timer:和Thread类似,含有定时器的线程,运行前要等待一段时间
Thread对象数据属性:
name:线程名
ident:线程标识符
daemon:是否是守护线程
Thread对象方法:
__init__(group=None, target=None, name=None, args=(), kwargs={}, verbose=None, daemon=None):实例化一个线程对象
start():执行线程
run():定义线程功能的方法,用于在子类中重写
join(timeout=None):等待指定线程执行结束直到超时
getName():返回线程名
setName(name):设置线程名
isAlive():线程是否存活
isDaemon():是否是守护线程
setDaemon(daemonic):设置守护线程标记,必须在start()之前设置才能生效
Thread类创建线程的三种通用解决方案:
1.创建Thread类的实例,传入一个函数
2.创建Thread类的实例,传入一个可调用的类实例
3.派生Thread类的子类,并创建子类的实例
python中函数是对象,因此给类定义__call__方法之后,可以使用方案2,但是代码比较晦涩而且本质上和方案1没有差别,因此一般使用方案1和方案3
一个使用子类的解决方案如下:
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
import threading
from time import sleep, ctime
loops = [4, 2]
#继承threading.Thread类的一个子类
class MyThread(threading.Thread):
def __init__(self, func, args, name = ''):
#调用父类的初始化函数
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
#重写run方法,使用start创建线程之后,会调用run方法
def run(self):
self.func(*self.args)
#线程方法
def loop(nloop, nsec):
print 'start loop ', nloop, ' at ', ctime()
sleep(nsec)
print 'loop ', nloop, ' done at ', ctime()
def main():
print 'starting at ', ctime()
threads = []
nloops = range(len(loops))
#创建threading.Thread的子类的实例
for i in nloops:
t = MyThread(loop, (i, loops[i]), loop.__name__)
threads.append(t)
#创建线程
for i in nloops:
threads[i].start()
#主线程等待所有线程结束
for i in nloops:
threads[i].join()
print 'all done at ', ctime()
if __name__ == '__main__':
main()
一般将子类单独做成一个模块,这样可以在其他python文件中import,通用模版如下:
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
import threading
from time import ctime
class MyThread(threading.Thread):
def __init__(self, func, args, name = ''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
#这样很巧妙,若func有返回值的话,可以调用此函数获取
def getResult(self):
return self.res
def run(self):
print 'starting', self.name, 'at', ctime()
self.res = self.func(*self.args)
print self.name, 'finished at', ctime()
threading中获取进程属性相关函数:
activeCount():当前活动的Thread对象个数
currentThread():返回当前Thread对象
enumerate():返回当前活动的Thread对象列表
settrace(func):为所有线程设置trace函数
setprofile(func):为所有线程设置profile函数
stack_size(size=0):返回新创建进程的栈大小,或者为后续线程设置栈大小为size