多线程定义的方法、线程锁、信号量、事件、TImer
Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
一、使用多线程
线程有两种调用方式、直接调用和继承式调用。
import threading,time
def hello(name):
print("my name is ",name)
time.sleep(3)
if __name__ == "__main__":
t1 = threading.Thread(target=hello,args=("zhangyan",)) #生成一个线程
t2 = threading.Thread(target=hello,args=("spring",)) #生成另一个线程
t1.start() #启动t1
t2.start() #启动t2
直接调用
import threading,time
class MyThread(threading.Thread): #一定要继承Thread类并且重写run方法
def __init__(self,name):
threading.Thread.__init__(self) #一定要显示的调用父类的构造函数
self.name = name
def run(self): #每个线程要运行的函数
print("my name is ",self.name)
time.sleep(3)
if __name__ == "__main__":
t1 = MyThread("zhangyan")
t2 = MyThread("spring")
t1.start()
t2.start()
继承式调用
构造方法:
Thread(group=None, target=None, name=None, args=(), kwargs={})
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 线程名; (可以在这里给线程起名字,也可以使用setName()方法设置名字)
args/kwargs: 要传入方法的参数。
更多方法:
start:线程准备就绪,等待CPU调度
setName:为线程设置名称
getName:获取线程名称
setDaemon:设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止,
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止。
join:等待子线程运行结束
import threading,time
def hello(name):
print("start...",name)
time.sleep(3)
print("end...",name)
if __name__ == "__main__":
t1 = threading.Thread(target=hello,args=("zhangyan",)) #生成一个线程
t2 = threading.Thread(target=hello,args=("spring",)) #生成另一个线程
t1.setDaemon(False) #设置一定要设置在start之前,默认就是False,不写也行
t2.setDaemon(False)
t1.start() #启动t1
t2.start() #启动t2
print("main thread end!") #主线程运行结束
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#运行结果
start... zhangyan
start... spring
main thread end!
end... zhangyan
end... spring
setDaemon(False)
多线程设置为前台线程,主线程结束程序并没有停止,而是其他线程也结束后才停止。
setDaemon()一定要设置在start()之前
import threading,time
def hello(name):
print("start...",name)
time.sleep(3)
print("end...",name)
if __name__ == "__main__":
t1 = threading.Thread(target=hello,args=("zhangyan",)) #生成一个线程
t2 = threading.Thread(target=hello,args=("spring",)) #生成另一个线程
t1.setDaemon(True) #设置为后台线程
t2.setDaemon(True)
t1.start() #启动t1
t2.start() #启动t2
print("main thread end!") #主线程结束
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#运行结果
start... zhangyan
start... spring
main thread end!
setDaemon(True)
多线程设置为后台线程,主线程结束后程序立刻停止,未等到后台线程运行结束。
import threading,time
def hello(name):
print("start...",name)
time.sleep(3)
print("end...",name)
if __name__ == "__main__":
thread_list = [] #创建线程存储的列表
for i in range(4):
t = threading.Thread(target=hello,args=str(i))
t.setDaemon(True)
thread_list.append(t)
for t in thread_list:
t.start()
for t in thread_list:
t.join()
print("main thread end!")
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#运行结果
start... 0
start... 1
start... 2
start... 3
end... 3
end... 2
end... 1
end... 0
main thread end!
join
也就是说,只有在多线程设置为后台线程时候,join才有用武之地。如果多线程设置为前台线程(未设置也是前台线程,默认的),程序本身就会等待全部线程运行结束后才停止,不需要join。
import threading,time
def hello(name):
print("start...",name)
time.sleep(3)
print("end...",name)
if __name__ == "__main__":
for i in range(4):
t = threading.Thread(target=hello,args=str(i))
t.setDaemon(True)
t.start()
t.join()
print("main thread end!")
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start... 0
end... 0
start... 1
end... 1
start... 2
end... 2
start... 3
end... 3
main thread end!
join的错误用法
也不能说是为错误用法,只是这个用法使得多线程又变成了单线程,使得多线程变得毫无意义。
创建每个线程的时候被设置了join,下一个线程就会等待这个线程结束之后才创建。
这也是之前的例子中需要使用thread_list来存储线程的原因,因为创建完毕之后还需要遍历一遍设置个join。
二、线程锁(Lock、RLock)
试想一下,多线程操作的时候是否可能会发生如下的情况。
在某一条线程对数据进行+1操作的时候,刚获取到数据还未进行+1(n条指令执行完毕),这时候CPU切换到了另一条线程上,这个线程也同样进行+1的操作。假如说原数据是3,那么这两个线程交给CPU运算的数据都是3,运算结束后变成了4,而不是设想中的5。
import threading,time
gl_num = 0
def show(arg):
global gl_num
time.sleep(1)
gl_num +=1
print(gl_num)
for i in range(10):
t = threading.Thread(target=show, args=(i,))
t.start()
print('main thread stop')
未使用锁
多次运行可能产生混乱,为了多个线程同时操作一个内存中的资源时不产生混乱,我们使用锁。
(python3中好像默认就使用了锁,所以在使用上述的例子的时候不会产生混乱。)
import threading,time
gl_num = 0
def show(arg):
global gl_num
time.sleep(1)
lock.acquire()
gl_num +=1
lock.release()
print(gl_num)
if __name__ == "__main__":
lock = threading.Lock()
for i in range(10):
t = threading.Thread(target=show, args=(i,))
t.start()
print('main thread stop')
Lock
RLock中除了状态locked和unlocked外还记录了当前lock的owner和递归层数,使得RLock可以被同一个线程多次acquire()。
三、信号量(Semaphore)
线程锁(互斥锁)同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
import threading, time
def run(n):
semaphore.acquire()
time.sleep(1)
print("run the thread: %s" % n)
semaphore.release()
if __name__ == '__main__':
num = 0
semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start()
Semaphore
四、事件(event)
事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
- isset:当内置标志为True时返回True。
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
import threading,time
import random
def light():
if not event.isSet():
event.set() #wait就不阻塞 #绿灯状态
count = 0
while True:
if count < 10:
print('\033[42;1m--green light on---\033[0m')
elif count <13:
print('\033[43;1m--yellow light on---\033[0m')
elif count <20:
if event.isSet():
event.clear()
print('\033[41;1m--red light on---\033[0m')
else:
count = 0
event.set() #打开绿灯
time.sleep(1)
count +=1
def car(n):
while 1:
time.sleep(random.randrange(10))
if event.isSet(): #绿灯
print("car [%s] is running.." % n)
else:
print("car [%s] is waiting for the red light.." %n)
if __name__ == '__main__':
event = threading.Event()
Light = threading.Thread(target=light)
Light.start()
for i in range(3):
t = threading.Thread(target=car,args=(i,))
t.start()
event
五、Timer
Timer(定时器)是Thread的派生类,用于在指定时间后调用一个方法。
Timer(interval, function, args=[], kwargs={})
interval: 指定的时间
function: 要执行的方法
args/kwargs: 方法的参数
import threading
def func():
print 'hello timer!'
timer = threading.Timer(5, func)
timer.start()
Timer