互斥锁、死锁和递归锁
一、互斥锁(Mutex)
在上节最后我们讲到了线程安全,线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading模块中定义了Lock类,可以方便的处理锁定:
#创建锁 lock = threading.Lock() #锁定 lock.acquire(blocking=True, timeout=-1) #释放 lock.release()
还是以上节实例为例:
# -*- coding: UTF-8 -*-
import threading
class MyThread(threading.Thread):
    def run(self):
        global n
        lock.acquire()
        n += 1
        lock.release()
        print(self.name + ' set n to ' + str(n))
n = 0
lock = threading.Lock()
if __name__ == '__main__':
    for i in range(5):
        t = MyThread()
        t.start()
    print('final num: %d' % n)输出:
Thread-1 set n to 1 Thread-2 set n to 2 Thread-3 set n to 3 Thread-4 set n to 4 Thread-5 set n to 5 final num: 5
加锁之后,我们可以确保数据的正确性
二、死锁
死锁是指一个资源被多次调用,而多次调用方都未能释放该资源就会造成一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
2.1 一个线程内部多次加锁却没有释放
import threading
class MyThread(threading.Thread):
    def run(self):
        global n1, n2
        lock.acquire()   # 加锁
        n1 += 1
        print(self.name + ' set n1 to ' + str(n1))
        lock.acquire()   # 再次加锁
        n2 += n1
        print(self.name + ' set n2 to ' + str(n2))
        lock.release()
        lock.release()
n1, n2 = 0, 0
lock = threading.Lock()
if __name__ == '__main__':
    thread_list = []
    for i in range(5):
        t = MyThread()
        t.start()
        thread_list.append(t)
    for t in thread_list:
        t.join()
    print('final num:%d ,%d' % (n1, n2))结果:
Thread-1 set n1 to 1 # 会一直等待
2.2 多个程序间相互调用引起死锁
import threading,time
 
def run1():
    print("grab the first part data")
    lock.acquire()
    global num
    num +=1
    lock.release()
    return num
def run2():
    print("grab the second part data")
    lock.acquire()
    global  num2
    num2+=1
    lock.release()
    return num2
def run3():
    lock.acquire()
    res = run1()
    print('--------between run1 and run2-----')
    res2 = run2()
    lock.release()
    print(res,res2)
 
 
if __name__ == '__main__':
 
    num,num2 = 0,0
    lock = threading.Lock()
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()
 
while threading.active_count() != 1:
    print(threading.active_count())
else:
    print('----all threads done---')
    print(num,num2) 三、递归锁
上述两个问题都可以使用python的递归锁解决
# 递归锁 rlock = threading.RLOCK()
RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。这里以例1为例,如果使用RLock代替Lock,则不会发生死锁:
# -*- coding: UTF-8 -*-
import threading
class MyThread(threading.Thread):
    def run(self):
        global n1, n2
        lock.acquire()   # 加锁
        n1 += 1
        print(self.name + ' set n1 to ' + str(n1))
        lock.acquire()   # 再次加锁
        n2 += n1
        print(self.name + ' set n2 to ' + str(n2))
        lock.release()
        lock.release()
n1, n2 = 0, 0
lock = threading.RLock()
if __name__ == '__main__':
    thread_list = []
    for i in range(5):
        t = MyThread()
        t.start()
        thread_list.append(t)
    for t in thread_list:
        t.join()
    print('final num:%d ,%d' % (n1, n2))输出:
Thread-1 set n1 to 1 Thread-1 set n2 to 1 Thread-2 set n1 to 2 Thread-2 set n2 to 3 Thread-3 set n1 to 3 Thread-3 set n2 to 6 Thread-4 set n1 to 4 Thread-4 set n2 to 10 Thread-5 set n1 to 5 Thread-5 set n2 to 15 final num:5 ,15
相关推荐
  chaigang    2020-06-13  
   hackerlpy    2020-09-25  
   鲁鲁酱    2020-06-02  
   sunnyJam    2020-03-27  
   fraternityjava    2020-03-01  
   ITxiaobaibai    2020-02-18  
   wanggongzhen    2020-01-09  
   tangjianft    2020-01-05  
   Attend    2010-09-04  
   farmanlinuxer    2011-04-01  
   kuailexiaochuan    2015-04-11  
   sprintwind    2012-06-06  
   wacsdn    2011-10-30  
   pointfish    2011-08-23  
   CloudCraft    2010-08-06  
   sqlican    2019-07-01  
   cleanerxiaoqiang    2019-06-30  
   cmsmdn    2018-10-07  
 