|
1.全局解释器锁定
Python虚拟机使用GIL(Global Interpreter Lock,全局解释器锁定)来互斥线程对共享资源的访问,暂时无法利用多处理器的优势。虽然python解释器可以“运行”多个线程,但在任意时刻,不管有多少的处理器,任何时候都总是只有一个线程在执行。对于I/O密集型任务,使用线程一般是没有问题的,而对于涉及大量CPU计算的应用程序而言,使用线程来细分工作没有任何好处,用户最好使用子进程和消息传递。
2.threading
python的threading模块提供Thread类和各种同步原语,用于编写多线程的程序。
2.1. Thread(target=None,name=None,args=(),kwargs={})
此函数创建一个Thread实例。target是一个可调用函数,线程启动时,run()方法将调用此对象。name是线程的名称,默认是'Thread-N'的格式。args是传递给target函数的参数元组,kwargs是传递给target函数的关键字参数的字典。
Thread实例t支持以下方法和属性:
- t.start() 启动线程,就是调用run()方法
- t.run() 可以在Thread的子类中重新定义
- t.join([timeout]) 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。
- t.is_live() 返回线程的活动状态
- t.name 线程的名称
- t.ident 线程标识符
- t.daemon 设置线程是否为守护线程,必须在t.start()前设置。当设置为True时,主线程要退出时,不必等守护线程完成。'
创建线程有两种方法:
import threading
import time
def clock(nsec):
whhile True:
print 'Now is %s'%time.ctime()
time.sleep(nsec)
t=threading.Thread(target=clock,args=(5,))
t.daemon=True #设置为守护线程
t.start()
2. 从Thread派生出一个子类,然后创建一个子类的实例
import threading
import time
class ClockThread(threading.Thread):
def __init__(self,nsec):
threading.Thread.__init__(self)
self.daemon=True #设置为守护线程
self.nsec=nsec
def run():
while True:
print 'Now is s%'%time.ctime()
time.sleep(self.nsec)
t=ClockThread(5)
t.start()
后一种方法比较python一点。
由于线程会无限循环,所以设置daemon为True,这样当进程结束时,线程也将被销毁。
例如有个数数程序,一个线程从1数到9,另一个线程从a数到j,每个线程都要耗费9s,如果要顺序执行的话需耗费18s。
import threading
import time
class CountThread(threading.Thread):
def __init__(self,func,name):
threading.Thread.__init__(self)
self.name=str(name)
self.func=func
def run(self):
apply(self.func)
def numcount():
print threading.currentThread().name,'start at : ',time.ctime()
for i in range(10):
print i
time.sleep(1)
print threading.currentThread().name,'done at : ',time.ctime()
def alphacount():
print threading.currentThread().name,'start at : ',time.ctime()
for i in range(97,107):
print chr(i)
time.sleep(1)
print threading.currentThread().getName(),'done at : ',time.ctime()
def main():
funclist=[numcount,alphacount]
threads=[]
for i in funclist:
t=CountThread(i,i.__name__)
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join()
print 'All done at :',time.ctime()
if __name__=='__main__':
main()
结果:
numcount start at : Fri Feb 07 12:19:28
alphacount start at : Fri Feb 07 12:19:28 2014
a0
b1
2c
3d
4
e
5f
6g
7
h
8
i
9j
alphacount numcount done at : done at : Fri Feb 07 12:19:38 2014
Fri Feb 07 12:19:38 2014
All done at : Fri Feb 07 12:19:38 2014
10s就完成了。
举一个更清晰的看t.join()作用的例子:
import threading
import time
def join():
print 'in Threadjoin'
time.sleep(1)
print 'out Threadjoin'
Threadjoin=threading.Thread(target=join,name='Threadjoin')
def context(Threadjoin):
print 'in Threadcontext'
Threadjoin.start()
Threadjoin.join() #Threadjoin线程开始阻塞,等待Threadjoin完成
print 'out Threadcontext'
Threadcontext=threading.Thread(target=context,name='Threadcontext',args=(Threadjoin,))
Threadcontext.start()
结果:
>>>
in Threadcontext
in Threadjoin
out Threadjoin
out Threadcontext
2.2. 线程的同步
线程运行在创建它的进程内部,共享所有的数据和资源,但都有自己独立的栈和堆。编写并发编程的难点在于同步和访问共享数据。多个任务同时更新一个数据结构可能导致数据损坏和程序状态不一致(也就是竞争条件)。要解决这个问题,必须找出程序的关键代码段,并使用互斥锁和其它类似的同步手段保护他们。
2.2.1 Lock
原语锁定(互斥锁定)是一个同步原语,状态是"已锁定"或"未锁定"之一。两个方法acquire()和release()用于修改锁定的状态。如果有多个线程在等待获取锁定,当锁定释放时,只有一个线程能获得它。
构造方法:
Lock() :创建新的Lock对象,初始状态为未锁定
实例方法:
Lock.acquire([timeout]): 使线程进入同步阻塞状态,尝试获得锁定。 成功获取锁定返回True,无法获取锁定返回False。
Lock.release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。
Python多线程分块读取大文件:
import threading
import os
seekposition=0
blocksize=1000000
filesize=0
def getFilesize(filename):
f=open(filename)
f.seek(0,os.SEEK_END)
filesize=f.tell()
f.close()
return filesize
def parsefile(filename):
global seekposition,filesize
f=open(filename)
while True:
lock.acquire() #seekposition是线程共享的,修改时需要锁定
startposition=seekposition
endposition=(startposition+blocksize) if (startposition+blocksize)0:
f.seek(startposition)
f.readline() #分成的block第一行可能不是完整的一行,略掉不处理,而是作为上一个block的最后一行处理
position=f.tell()
outfile=open(str(endposition)+'.txt','w')
while position |
|