1. 多线程的基本使用
import threading import time def run(num): print('Num: %s'% num) time.sleep(3) if num == 4: print('Thread is finished.')
# 对函数 run 创建5个线程 for i in range(5): # 创建线程,target:目标函数,args:函数的参数,使用元组的形式 t = threading.Thread(target=run,args=(i,)) # 开始线程活动 t.start() time.sleep(0.01) print('Main thread is finished.')
结果:
Num: 0 Num: 1 Num: 2 Num: 3 Num: 4 Main thread is finished. Thread is finished.
# 上面打印的顺序是先打印 Main thread is finished.因为主线程已经完成了,而子线程里 slee(3),所以此时子线程尚未完成,大约3秒后,才打印的 Thread is finished.
2. 等待子线程执行:join
import threading import time def run(num): print('Num: %s'% num) time.sleep(3) if num == 4: print('Thread is finished.') Tlist = [] for i in range(5): # 创建线程,target:目标函数,args:函数的参数,使用元组的形式 t = threading.Thread(target=run,args=(i,)) # 开始线程活动 t.start() Tlist.append(t) for t in Tlist: # 针对每个子线程,等待子线程执行一段时间,再继续往下执行主线程。 # t.join(0.1) # 针对每个子线程,等待0.1秒,继续执行主线程 t.join() # 没写时间,则默认等待子线程执行完毕,才继续往下执行主线程。 time.sleep(0.01) print('Main thread is finished.')
结果: Num: 0 Num: 1 Num: 2 Num: 3 Num: 4 Thread is finished. Main thread is finished.
# 子线程使用了join方法,等待子线程执行完毕才继续执行主线程,所以打印顺序是正常的。
3. 守护线程
即只要主线程执行完毕,不管子线程状态如何,都会被强制杀掉。
import threading import time def run(num): print('Num: %s'% num) time.sleep(3) if num == 4: print('Thread is finished.') Tlist = [] for i in range(5): # 创建线程,target:目标函数,args:函数的参数,使用元组的形式 t = threading.Thread(target=run,args=(i,)) # 设置为守护线程 t.setDaemon(True) # 开始线程活动 t.start() Tlist.append(t) for t in Tlist: # 针对每个子线程,等待子线程执行一段时间,再继续往下执行主线程。 t.join(0.1) # 对子线程等待0.1秒后,继续执行主线程 # t.join() # 没写时间,则默认等待子线程执行完毕,才继续往下执行主线程。 time.sleep(0.01) print('Main thread is finished.') print('Current Thread:',threading.activeCount()) # 打印当前活动线程数量,这是主线程最后一个语句,所以不管此时还有多少个活动的守护线程,当此句执行完毕,守护线程都会被杀掉。
结果:
Num: 0 Num: 1 Num: 2 Num: 3 Num: 4 Main thread is finished. Current Thread: 6
# 少打印了 Thread is finished. 因为 主线程结束后,子线程就被杀掉了。
4. 线程锁
GIL锁: Global Interpreter Lock,全局解释器锁。为了解决多线程之间数据完整性和状态同步的问题,在任意时刻只有一个线程在解释器中运行。这是CPython中的问题。
线程安全:在多线程中,共享的数据同一时间只能由一个线程执行。
互斥锁:(threading.Lock) 并不关心当前是哪个线程占有了该锁;如果该锁已经被占有了,那么任何其它尝试获取该锁的线程都会被阻塞,包括已经占有该锁的线程也会被阻塞。
递归锁:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源(已经占用该锁的线程,可以多次actuire,并不会阻塞)
死锁:死锁就是当一个线程已经占用了锁并且尚未释放,但是又有新的获取该锁的请求,亦或着两个线程分别占据着对方想要获取的锁,此时线程进入了阻塞的状态,就是死锁。
部份转自:https://www.jb51.net/article/74426.htm
部份转自:https://www.cnblogs.com/ArsenalfanInECNU/p/10022740.html
e.g.
互斥锁:threading.Lock
import threading class MyThread(threading.Thread): def run(self): # 这是重写的父类的run方法 global num if mutex.acquire(): # 判断能否加锁,acquire()返回的是加锁的状态,加锁成功就是True num = num+1 msg = self.name+' set num to '+str(num) print(msg) # mutex.acquire() # 这两句话如果不注释掉,就会进入死锁的状态。因为上一个锁还未释放,再次请求加锁,就会阻塞。 # mutex.release() mutex.release() # 释放锁 num = 0 mutex = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
普通方法:
import threading import time def run(n): global num if mutex.acquire(): # 返回获取锁的结果,加锁成功为True,失败false num = num+1 msg ='Thread-%s set num to ' %n + str(num) print(msg) mutex.release() time.sleep(2) num = 0 mutex = threading.Lock() for i in range(5): t = threading.Thread(target=run,args=(i+1,)) t.start()
递归锁:threading.RLock
import threading class MyThread(threading.Thread): def run(self): global num if mutex.acquire(): num = num+1 msg = self.name+' set num to '+str(num) print(msg) mutex.acquire() # 虽然再次acquire,但是并不会阻塞 mutex.release() mutex.release() num = 0 mutex = threading.RLock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()