一、Thread 的生命周期
1、创建对象时,代表 Thread 内部被初始化。
2、调用 start() 方法后,thread 会开始运行。
3、thread 代码正常运行结束或者是遇到异常,线程会终止。
可以通过 Thread 的 is_alive() 方法查询线程是否还在运行。
值得注意的是,is_alive() 返回 True 的情况是 Thread 对象被正常初始化,start() 方法被调用,然后线程的代码还在正常运行
import threading
import time
def test():
for i in range(5):
print(threading.current_thread().name+' test ',i)
time.sleep(0.5)
thread = threading.Thread(target=test,name='TestThread')
# thread = threading.Thread(target=test)
thread.start()
for i in range(5):
print(threading.current_thread().name+' main ', i)
print(thread.name+' is alive ', thread.isAlive())
time.sleep(1)
二、在线程里面setDaemon()和join()方法都是常用的,他们的区别如下
(1)join ()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,
才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。join([timeout]) 里面的参数时可选的,代表线程运行的最大时
间,即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,然后主线程或函数都会接着执行的,如果线程执行时间小于参数表示的
时间,则接着执行,不用一定要等待到参数表示的时间。
setDaemon()方法。主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这
时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有
个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起,只有等待了所有线程结束它才结束。
二、线程锁的种类
1. Lock() 同步锁 一次一个
2. RLock() 递归锁 一次一个
3.BoundedSemaphore() 一次放N个 信号量
4.Condition() 一次放X个
5. Evvent() 事件 一次放所有
6.线程池ThreadPoolExecutor()
1、锁(Lock)对象
原始锁(primitive lock),当它锁住的时候,它是一种不属于任何一个线程的同步原语(synchronization primitive)。 在Python中,他是目前可用的最底层的同步原语,由_thread模块提供。
import time
import threading
lock = threading.Lock() # 同步锁
l = []
def fun(arg):
lock.acquire()
l.append(arg)
time.sleep(0.01)
e = l[-1]
print(arg,e)
lock.release()
for i in range(10):
t = threading.Thread(target=fun, args=(i,))
t.start()
RLock对象
可重入锁(reentrant lock)。感觉是一个锁中锁,就是可以递归的锁。等见到具体的应用例子再写。
import time
import threading
lock = threading.RLock() # 递归锁
l = []
def fun(arg):
lock.acquire()
lock.acquire() # 可以锁多次
l.append(arg)
time.sleep(0.01)
e = l[-1]
print(arg,e)
lock.release()
lock.release()
for i in range(10):
t = threading.Thread(target=fun, args=(i,))
t.start()
Condition对象
condition变量总是与某种锁相关,锁可以是传过来的,也可以通过默认设置创建。如果有多个 condition对象需要共享一个锁时,传递一个锁是非常有用的。锁是condition对象的一部分,你不用刻意的跟踪它。
# 方式一
import time
import threading
lock = threading.Condition()
l = []
def fun(arg):
lock.acquire()
lock.wait()
l.append(arg)
time.sleep(0.01)
e = l[-1]
print(arg,e)
lock.release()
for i in range(20):
t = threading.Thread(target=fun, args=(i,))
t.start()
while True:
inp = int(input('执行的个数:'))
lock.acquire()
lock.notify(inp)
lock.release()
# 方式二
import time
import threading
lock = threading.Condition()
l = []
def f():
print('执行:')
input('>>>')
ct = threading.current_thread() # 获取当前线程
ct_name = ct.getName() # 获取当前线程名
print(ct, ct_name)
return True # 返回一个True执行
def fun(arg):
lock.acquire()
lock.wait_for(f)
l.append(arg)
time.sleep(0.01)
e = l[-1]
print(arg,e)
lock.release()
for i in range(20):
t = threading.Thread(target=fun, args=(i,))
t.start()
Semaphore类和BoundedSemaphore类都是信号量类
每次有线程获得信号量的时候(即acquire())计数器-1,释放信号量时候(release())计数器+1,计数器为0的时候其它线程就被阻塞无法获得信号量。当计数器为设定好的上限的时候BoundedSemaphore就无法进行release()操作了,Semaphore没有这个限制检查
import time
import threading
lock = threading.BoundedSemaphore(4)
l = []
def fun(arg):
lock.acquire()
l.append(arg)
time.sleep(1)
e = l[-1]
print(arg,e)
lock.release()
for i in range(20):
t = threading.Thread(target=fun, args=(i,))
t.start()
import threading
import time
num = 2
def test():
global num
n = se.acquire()
num -= 1
print("Semaphore acquire:",num)
time.sleep(1)
se.release()
num += 1
print("Semaphore release:",num)
if __name__ == "__main__":
se = threading.Semaphore(num)
se.release()
tlist = []
for i in range(5):
t = threading.Thread(target=test)
tlist.append(t)
for i in tlist:
i.start()
print("main")
Evvent() 事件 一次放所有
通过threading.Event()可以创建一个事件管理标志,该标志(event)默认为False,event对象主要有四种方法可以调用:
event.wait(timeout=None):调用该方法的线程会被阻塞,如果设置了timeout参数,超时后,线程会停止阻塞继续执行;
event.set():将event的标志设置为True,调用wait方法的所有线程将被唤醒;
event.clear():将event的标志设置为False,调用wait方法的所有线程将被阻塞;
event.isSet():判断event的标志是否为True。
import time
import threading
lock = threading.Event()
def fun(arg):
print('执行:',arg)
lock.wait() # 加锁卡住
print(arg)
for i in range(20):
t = threading.Thread(target=fun, args=(i,))
t.start()
lock.set() # 释放锁
lock.clear() # 清楚锁,再次枷锁
线程池ThreadPoolExecutor()
-
ThreadPoolExecutor
构造实例的时候,传入max_workers
参数来设置线程池中最多能同时运行的线程数目。 - 使用
submit
函数来提交线程需要执行的任务(函数名和参数)到线程池中,并返回该任务的句柄(类似于文件、画图),注意submit()
不是阻塞的,而是立即返回。 - 通过
submit
函数返回的任务句柄,能够使用done()
方法判断该任务是否结束。上面的例子可以看出,由于任务有2s的延时,在task1
提交后立刻判断,task1
还未完成,而在延时4s之后判断,task1
就完成了。 - 使用
cancel()
方法可以取消提交的任务,如果任务已经在线程池中运行了,就取消不了。这个例子中,线程池的大小设置为2,任务已经在运行了,所以取消失败。如果改变线程池的大小为1,那么先提交的是task1
,task2
还在排队等候,这是时候就可以成功取消。 - 使用
result()
方法可以获取任务的返回值。查看内部代码,发现这个方法是阻塞的。
from concurrent.futures import ThreadPoolExecutor
import time
def task(arg,arg2):
time.sleep(1)
print(arg,arg2)
# 创建线程池,最多5个
pool = ThreadPoolExecutor(5)
for i in range(8):
# 去线程池中申请一个线程,让线程执行task函数
pool.submit(task,i,8)