一、线程基础
在多线程编程出现之前,程序是一个执行序列,按顺序在CPU中运行的,即使程序的子任务之间相互独立,也要按顺序执行。为了提高效率,能将各种本质上就是异步的子任务做并行处理的编程方式应运而生了,这就是多线程编程。
线程有时也被称为轻量级进程,具有开始、顺序执行、结束三部分。与进程不同,线程之间可以直接共享运行的结果,所以线程可以用一个指令指针来记录自己运行到什么地方,然后通过中断或是睡眠的方式,让步给其他线程,这种快速的切换就让使用者感觉程序是在并行执行了。Python解释器要实现多线程,也必须在执行多个线程的同时,保证在任意时刻,在解释器中运行的线程也只有一个。要实现这个目标依靠的是全局解释锁GIL,线程在运行前先设置一个GIL,运行结束后再将它解锁。
Python中主要使用threading模块来产生线程和锁,利用Queue模块在线程间进行通信。
threading模块:
Thread 表示一个线程的执行的对象
Lock 锁原语对象
RLock 可重入锁对象。使单线程可以再次获得已经获得的锁
Condition 条件变量对象能让一个线程停下来,等待其他线程满足了某个“条件”,如状态的改变或值的改变
Event 通用的条件变量。多个线程可以等待某个事件发生,在事件发生后,所有的线程都被激活
Semaphore 为等待锁的线程提供一个类似“等候室”的结构
BoundedSemaphore 与semaphore类似,只是它不允许超过初始值
Timer 与Thread类似,只是,它要等待一段时间后才开始运行
Thread 类的接口:
Thread(group=None, target=None, name=None, args=(), kwargs={})
构造方法:
group: 线程组,目前还没有实现,库引用中提示必须是None
target: 要执行的方法
name: 线程名
args/kwargs: 要传入方法的参数
Thread类的方法:
getName(self) 返回线程的名字
isAlive(self) 布尔标志,表示这个线程是否还在运行中
isDaemon(self) 返回线程的daemon标志
join(self, timeout=None) 程序挂起,直到线程结束,如果给出timeout,则最多阻塞timeout秒
run(self) 定义线程的功能函数
setDaemon(self, daemonic) 把线程的daemon标志设为daemonic
setName(self, name) 设置线程的名字
start(self) 开始线程执行
其中Thread类是主要的运行对象,它有很多函数,可以用多种方法来创建线程,常用的为以下两种:
创建一个Thread的实例,将要执行的方法作为参数传给Thread的构造方法。
从Thread继承一个子类,并重写run()
实例1:
#!/usr/bin/python
import threading,time
def func(x,y):
if x>y:
print x
else:
print y
th1=threading.Thread(target=func,args=(1,2))
th1.start()
class myThread(threading.Thread):
def run(self):
func(1,2)
th2=myThread()
th2.start()
#Output:
# 2
# 2
实例2:
setName(self, name) 设置线程的名字
#!/usr/bin/python
import threading
class MyThread(threading.Thread):
def __init__(self,a,name=None):
threading.Thread.__init__(self,name=None)
self.a=a
def run(self):
print self.name,self.a
newThread=MyThread(12345,name='MyThread1')
#newThread.setName('newThread')
newThread.start()
#Output:
# Thread-1 12345
#
比较:
#!/usr/bin/python
import threading
class MyThread(threading.Thread):
def __init__(self,a,name=None):
threading.Thread.__init__(self,name=None)
self.a=a
def run(self):
print self.name,self.a
newThread=MyThread(12345,name='MyThread1')
newThread.setName('newThread')
newThread.start()
#Output:
# newThread 12345
#
实例3:
setDaemon() 设置daemon ,必须设置在start()之前,当没有活动的非守护线程时,整个python程序退出。daemon设置为True,主线程退出了子线程跟着退出。
#!/usr/bin/python
import threading,time
def test():
for i in range(5):
print i
time.sleep(10)
newThread=threading.Thread(target=test,name='newThread')
newThread.setDaemon(True)
#newThread.setDaemon(False)
newThread.start()
#newThread.join(5)
print newThread.isDaemon()
print "main over!"
#Output
# 0
# True
# main over!
#
比较:
#!/usr/bin/python
import threading,time
def test():
for i in range(5):
print i
time.sleep(10)
newThread=threading.Thread(target=test,name='newThread')
newThread.start()
#newThread.join(5)
print newThread.isDaemon()
print "main over!"
#Output
# 0
# False
# main over!
# 1
# 2
# 3
# 4
实例4:
等待线程完成
设置join(),等待至线程中止。阻塞调用线程至线程的join()方法被调用中止,正常退出或抛出异常,或者超时。
join方法原型如下:
join([timeout])
timeout可选参数,线程运行的最长时间。
import threading,time
def test():
for i in range(5):
print i
time.sleep(10)
newThread=threading.Thread(target=test,name='newThread')
newThread.start()
newThread.join()
print newThread.isDaemon()
print "main over!"
#Output
# 0
# 1
# 2
# 3
# 4
# False
# main over!
比较:
import threading,time
def test():
for i in range(5):
print i
time.sleep(10)
newThread=threading.Thread(target=test,name='newThread')
newThread.start()
newThread.join(9)
print newThread.isDaemon()
print "main over!"
#Output
# 0
# False
# main over!
# 1
# 2
# 3
# 4
二、线程同步,避免并发问题
RLock (可重入锁定)是Lock(单一线程锁定)的一种变化形式,都具有acquire()和release()方法 ,如果需要每次只有一个线程操作的数据,可以将操作过程放在acquire方法与release方法之间,保持线程同步。如下:
#!/usr/bin/python
import threading,time
rlock = threading.RLock()
class working:
data=0
def Add(self):
i=0
while i < 10000:
working.data+=1
i+=1
class Mythread(threading.Thread):
def run(self):
new1=working()
if rlock.acquire():
new1.Add()
rlock.release()
print working.data ,"at", time.ctime()
def main():
t1=Mythread()
t2=Mythread()
t3=Mythread()
t1.start()
t2.start()
t3.start()
if __name__=='__main__':
main()
#Output:
# 10000 at Mon Aug 29 14:07:14 2011
# 20000 at Mon Aug 29 14:07:14 2011
# 30000 at Mon Aug 29 14:07:14 2011
如果注释掉
if rlock.acquire()与rlock.release()这两行,则输出的数据将不可测,如下:
6661 at Tue Aug 30 09:16:47 2011
20214 at Tue Aug 30 09:16:47 2011
22625 at Tue Aug 30 09:16:47 2011
多线程Ping实例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/python
import re,threading,subprocess
#configure IP
startip='192.168.1.1'
endip='192.168.1.100'
class IpList:
net_seg=[]
host_bit=[]
ipaddr=[]
count_start=None
count_end=None
def __init__(self,startip,endip):
self.startip=startip
self.endip=endip
get_seg=re.compile(r'^\d{1,3}.\d{1,3}.\d{1,3}.')
get_host=re.compile(r'\d{1,3}$')
h_seg=get_seg.search(self.startip).group()
h_begin=get_host.search(self.startip).group()
h_end=get_host.search(self.endip).group()
IpList.count_start=int(h_begin)
IpList.count_end=int(h_end)+1
IpList.net_seg.append(h_seg)
for i in range(IpList.count_start,IpList.count_end):
IpList.host_bit.append(i)
ip=IpList.net_seg[0]+str(IpList.host_bit.pop(0))
IpList.ipaddr.append(ip)
class LifeHost(threading.Thread):
mark= re.compile(r"(\d) received")
init_status=("Unknown","Alive")
def __init__(self,ip):
threading.Thread.__init__(self)
self.ip=ip
def run(self):
self.cmd="ping -q -c1 "+self.ip
self.result=subprocess.Popen(self.cmd,stdout=subprocess.PIPE,shell=True)
self.result_data=self.result.stdout.read()
self.mark_value=re.findall(LifeHost.mark,self.result_data)
self.status_value=int(self.mark_value[0])
self.status=LifeHost.init_status[self.status_value]
print "%s is %s" %(self.ip,self.status)
def main():
init=IpList(startip,endip)
for i in range(int(init.count_end-1)):
pinghost=LifeHost(init.ipaddr[i])
pinghost.start()
if __name__=='__main__':
main()
使用Queue类来建立简单的安全线程,实例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/python
import re,threading,subprocess,Queue
Thread_LIMIT=100
#configure IP
startip='192.168.1.1'
endip='192.168.1.254'
class IpList:
net_seg=[]
host_bit=[]
ipaddr=[]
def __init__(self,startip,endip):
self.startip=startip
self.endip=endip
get_seg=re.compile(r'^\d{1,3}.\d{1,3}.\d{1,3}.')
get_host=re.compile(r'\d{1,3}$')
h_seg=get_seg.search(self.startip).group()
h_begin=get_host.search(self.startip).group()
h_end=get_host.search(self.endip).group()
countStart=int(h_begin)
countEnd=int(h_end)+1
IpList.net_seg.append(h_seg)
for i in range(countStart,countEnd):
IpList.host_bit.append(i)
ip=IpList.net_seg[0]+str(IpList.host_bit.pop(0))
IpList.ipaddr.append(ip)
class PutIpQueue(threading.Thread):
def __init__(self,queue,ip):
threading.Thread.__init__(self)
self.queue=queue
self.ip=ip
def run(self):
self.queue.put(self.ip)
class LifeHost(threading.Thread):
mark= re.compile(r"(\d) received")
init_status=("Unknown","Alive")
def __init__(self,queue):
threading.Thread.__init__(self)
self.queue=queue
def run(self):
while (not self.queue.empty()):
self.ip=self.queue.get()
self.queue.task_done()
self.cmd="ping -q -c1 "+self.ip
self.result=subprocess.Popen(self.cmd,stdout=subprocess.PIPE,shell=True)
self.result_data=self.result.stdout.read()
self.mark_value=re.findall(LifeHost.mark,self.result_data)
self.status_value=int(self.mark_value[0])
self.status=LifeHost.init_status[self.status_value]
print "%s is %s " %(self.ip,self.status)
def main():
queue=Queue.Queue()
init=IpList(startip,endip)
while (len(init.ipaddr)!=0):
getip=PutIpQueue(queue,init.ipaddr.pop(0))
getip.start()
for n in range(Thread_LIMIT):
pinghost=LifeHost(queue)
pinghost.start()
if __name__=='__main__':
main()
python中的Queue对象也提供了对线程同步的支持。将线程与队列编织在一起,把任务放进队列中去,然后开n个线程,每个线程去队列中取一个任务,执行完了之后告诉系统执行完了,然后接着去队列中取下一个任务,直到队列中所有的任务取空,退出线程。
生成队列对象(可选参数maxsize指定队列长度,默认为0):
queue=Queue.Queue(maxsize=0)
queue.put(i) 调用put方法将变量i的值添加到队列中。
queue.get() 调用get方法获取队列中的内容。
queue.task_done() 调用task_done方法,告诉系统任务完成。
queue.empty() empty方法用于判断队列是否为空。空返回True,非空则返回False
queue.full() full方法用于判断队列是否满。满返回True,否则返回False
注:上面两个ping实例,采用将操作数据放入队列中的方法,实现线程同步。下面实例将操作任务放入队列中实现线程同步。这种方法更可取。
实例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/python
#_*_coding: utf-8_*_
import re,threading,subprocess,Queue,string
MAX_LIMIT= 10
#configure IP
startip= '192.168.1.1'
endip= '192.168.1.254'
#IpList类用于将特定范围的ip地址写入ipaddr列表中。参数startip、endip分别为起始ip地址与结尾ip地址。
class IpList:
net_seg= []
host_bit= []
ipaddr= []
def __init__(self,startip,endip):
self.startip= startip
self.endip= endip
get_seg= re.compile(r'^\d{1,3}.\d{1,3}.\d{1,3}.')
get_host= re.compile(r'\d{1,3}$')
h_seg= get_seg.search(self.startip).group()
h_begin= get_host.search(self.startip).group()
h_end= get_host.search(self.endip).group()
countStart= int(h_begin)
countEnd= int(h_end)+1
IpList.net_seg.append(h_seg)
for i in range(countStart,countEnd):
IpList.host_bit.append(i)
ip= IpList.net_seg[0]+str(IpList.host_bit.pop(0))
IpList.ipaddr.append(ip)
#PutIpQueue类执行一个操作任务,将参数ip转来的值(即ip地址)加入queue队列中。
class PutIpQueue(threading.Thread):
def __init__(self,queue,ip):
threading.Thread.__init__(self)
self.queue= queue
self.ip= ip
def run(self):
# while (not self.queue.full()):
self.queue.put(self.ip)
#LifeHost类执行一个操作任务,从queue队列中获取一个ip地址,执行ping命令并打印相关状态。
class LifeHost(threading.Thread):
mark= re.compile(r"(\d) received")
init_status= ("Unknown","Alive")
def __init__(self,queue):
threading.Thread.__init__(self)
self.queue= queue
def run(self):
while (not self.queue.empty()):
self.ip= self.queue.get()
self.queue.task_done()
self.cmd= string.join(['ping -q -c1 ',self.ip])
self.result= subprocess.Popen(self.cmd,stdout=subprocess.PIPE,shell=True)
self.result_data= self.result.stdout.read()
self.mark_value= re.findall(LifeHost.mark,self.result_data)
self.status_value= int(self.mark_value[0])
self.status= LifeHost.init_status[self.status_value]
print "%s is %s " %(self.ip,self.status)
return
def main():
#实例化Queue类的对象queue。queue为一个列表。
queue= Queue.Queue(maxsize=MAX_LIMIT)
#初始化操作,创建IpList的实例init。定义两个空列表,用于存放操作任务。
init= IpList(startip,endip)
plist= []
clist= []
while (len(init.ipaddr)!=0):
#创建两个类的实例p,c两个操作任务,分别将这两个操作任务加入任务列表中。
#注:参数queue传递的只是名称到对象queue的绑定,而不是赋值,赋值应为queue[:]。
p= PutIpQueue(queue,init.ipaddr.pop(0))
c= LifeHost(queue)
plist.append(p)
clist.append(c)
#分别取出任务队列中的任务,并执行。
for i in plist:
i.start()
for i in clist:
i.start()
if __name__=='__main__':
main()
<...>