一、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()

  1. ThreadPoolExecutor构造实例的时候,传入max_workers参数来设置线程池中最多能同时运行的线程数目。
  2. 使用submit函数来提交线程需要执行的任务(函数名和参数)到线程池中,并返回该任务的句柄(类似于文件、画图),注意submit()不是阻塞的,而是立即返回。
  3. 通过submit函数返回的任务句柄,能够使用done()方法判断该任务是否结束。上面的例子可以看出,由于任务有2s的延时,在task1提交后立刻判断,task1还未完成,而在延时4s之后判断,task1就完成了。
  4. 使用cancel()方法可以取消提交的任务,如果任务已经在线程池中运行了,就取消不了。这个例子中,线程池的大小设置为2,任务已经在运行了,所以取消失败。如果改变线程池的大小为1,那么先提交的是task1task2还在排队等候,这是时候就可以成功取消。
  5. 使用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)