首页 > 代码库 > Python进阶(3)_进程与线程中的lock(互斥锁、递归锁、信号量)

Python进阶(3)_进程与线程中的lock(互斥锁、递归锁、信号量)

1、同步锁 (Lock)

当各个线程需要访问一个公共资源时,会出现数据紊乱

例如:

 1 import threading,time
 2 def sub():
 3     global num         #对全局变量进行操作
 4 
 5     temp=num
 6     time.sleep(0.001)    #模拟线程执行中出现I/o延迟等
 7     num=temp-1           #所有线程对全局变量进行减一
 8 
 9     time.sleep(1)
10 
11 num=100
12 l=[]
13 
14 for i in range(100):
15     t=threading.Thread(target=sub,args=())
16     t.start()
17     l.append(t)
18 
19 for obj in l:
20     obj.join()
21 
22 print(num)          
23 
24 #执行结果不可预期:
25 >>:90
26 >>:93
27 >>:92
28 

  当全局资源(counter)被抢占的情况,问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。在开发过程中我们必须要避免这种情况,那怎么避免?这就用到了互斥锁了。

互斥锁概念

  Python编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。在Python中我们使用threading模块提供的Lock类。

  我们对上面的程序进行整改,为此我们需要添加一个互斥锁变量lock = threading.Lock(),然后在争夺资源的时候之前我们会先抢占这把锁lock.acquire(),对资源使用完成之后我们在释放这把锁mutex.release()。

代码如下:

import threading,time
def sub():
    global num

    lock.acquire()
    temp=num
    time.sleep(0.01)
    num=temp-1
    lock.release()

    time.sleep(1)

num=100
l=[]
lock=threading.Lock()
for i in range(100):
    t=threading.Thread(target=sub,args=())
    t.start()
    l.append(t)

for obj in l:
    obj.join()

print(num)

2、死锁与递归锁

  所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 

 

会产生死锁的例子:

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        self.foo()


    def foo(self):
        LockA.acquire()
        print(‘I am %s GET LOCKA---------%s‘%(self.name,time.ctime()))
        LockB.acquire()
        print(‘I am %s GET LOCKB---------%s‘ % (self.name, time.ctime()))

        LockB.release()

        LockA.release()

LockA=threading.Lock()
LockB=threading.Lock()

for i in range(10):
    t=MyThread()
    t.start()

使用递归锁解决:

  在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        self.foo()
        self.bar()

    def foo(self):
        RLock.acquire()
        print(‘I am %s GET LOCKA---------%s‘%(self.name,time.ctime()))
        RLock.acquire()
        print(‘I am %s GET LOCKB---------%s‘ % (self.name, time.ctime()))

        RLock.release()
        RLock.release()

    def bar(self):

        RLock.acquire()
        print(‘I am %s GET LOCKB---------%s‘ % (self.name, time.ctime()))
        time.sleep(1)
        RLock.acquire()
        print(‘I am %s GET LOCKA---------%s‘ % (self.name, time.ctime()))

        RLock.release()
        RLock.release()

RLock=threading.RLock()

for i in range(10):
    t=MyThread()
    t.start()

  

3、Semaphore(信号量)

Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):

 1 import threading
 2 import time
 3 
 4 semaphore = threading.Semaphore(5)
 5 
 6 def func():
 7     if semaphore.acquire():
 8         print (threading.currentThread().getName() +  get semaphore)
 9         time.sleep(2)
10         semaphore.release()
11 
12 for i in range(20):
13   t1 = threading.Thread(target=func)
14   t1.start()

 

Python进阶(3)_进程与线程中的lock(互斥锁、递归锁、信号量)