线程锁(Mutex)互斥锁
以上演示的是GIL锁,GIL锁只能保证同一时刻,CPU上只有一条线程在运行,但不能保证同一时刻只有一条线程在修改数据,要想实现这个效果,我们要加上另一把锁,线程锁。示例如下:
import threading import time start_time = time.time() def run(n): lock.acquire() #获取线程锁对象 global num # time.sleep(1) num += 1 lock.release() #释放线程锁 lock = threading.Lock() #示例化一个线程锁对象 num = 0 t_l = [] #存放线程 for i in range(10): t = threading.Thread(target=run, args=('t-%s' % i, )) t.start() t_l.append(t) #为了不阻塞其他线程的启动,先放在列表里,最后join for t in t_l: #循环线程列表,执行所有线程 t.join() print('All threads have ran down!', threading.current_thread(), threading.activeCount()) time.sleep(0) #current_thread显示当前线程 #activeCount显示活动的线程数 print('Num: ', num) print('Total Cost: ', time.time() - start_time)
运行结果
All threads have ran down! <_MainThread(MainThread, started 7268)> 1 Num: 10 Total Cost: 0.0026278495788574
这次结果就对了,但此时实际上是串行运行。
递归锁
说白了就是在一个大锁中还要再包含子锁
示例
import threading def run1(): print("grab the first part data") lock.acquire() #第二把锁 global num #声明全局变量,让全局变量num可以在run1()内进行修改 num += 1 lock.release() #run1()执行完毕,释放第二把锁 return num #返回run3()num的值 def run2(): print("grab the second part data") lock.acquire() global num2 num2 += 2 lock.release() return num2 def run3(): lock.acquire() #加递归锁,第一把锁 res = run1() #执行run1()函数 print('--------between run1 and run2-----') res2 = run2() lock.release() #释放第一把锁 print(res, res2) if __name__ == '__main__': num, num2 = 0, 0 lock = threading.RLock() #RLock递归锁,如果是Lock,程序会陷入死循环 for i in range(10): t = threading.Thread(target=run3) #每循环一次,启动一条线程执行run3() t.start() while threading.active_count() != 1: #循环条件,活动线程数为1时循环结束 print('From Primary Thread: Thread left: ', threading.active_count()) else: print('All threads are done!') print('Num1: %d, Num2: %d' %(num, num2))
输出结果
grab the first part data --------between run1 and run2----- grab the second part data 1 2 grab the first part data --------between run1 and run2----- grab the second part data 2 4 grab the first part data --------between run1 and run2----- grab the second part data 3 6 grab the first part data --------between run1 and run2----- grab the second part data 4 grab the first part data --------between run1 and run2----- 8grab the second part data 5grab the first part data 10--------between run1 and run2----- grab the second part data 6 grab the first part data12 --------between run1 and run2----- grab the second part data 7grab the first part data 14From Primary Thread: Thread left: --------between run1 and run2----- 5grab the second part data From Primary Thread: Thread left: 4 From Primary Thread: Thread left: 8grab the first part data4 16--------between run1 and run2-----From Primary Thread: Thread left: grab the second part data4 9grab the first part data From Primary Thread: Thread left: 18 --------between run1 and run2----- 3 grab the second part dataFrom Primary Thread: Thread left: 102 From Primary Thread: Thread left: 202 All threads are done! Num1: 10, Num2: 20
上面示例中
lock = threading.RLock()
这里如果使用Lock,而不是RLock,程序会陷入死循环。