为什么要有三把锁
学习三把锁时候我们需要先知道为什么要有三把锁
全局资源(counter)被抢占的情况,问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。在开发过程中我们必须要避免这种情况,那怎么避免?这就用到了我们在综述中提到的互斥锁了。
同步锁 (Lock)又名互斥锁
互斥锁概念
Python编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。在Python中我们使用threading模块提供的Lock类。
我们对上面的程序进行整改,为此我们需要添加一个互斥锁变量mutex = threading.Lock(),然后在争夺资源的时候之前我们会先抢占这把锁mutex.acquire(),对资源使用完成之后我们在释放这把锁mutex.release()。代码如下:
import threading import time counter = 0 mutex = threading.Lock() #创建进程所 class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): global counter, mutex time.sleep(1); if mutex.acquire(): #判断是否存在进程锁 counter += 1 print "I am %s, set counter:%s" % (self.name, counter) mutex.release() 释放进程所 if __name__ == "__main__": for i in range(0, 100): my_thread = MyThread() my_thread.start()
递归锁(RLock)
在threading模块中,定义两种类型的琐:threading.Lock和threading.RLock。它们之间有一点细微的区别,通过比较下面两段代码来说明:
import threading lock = threading.Lock() #Lock对象 lock.acquire() lock.acquire() #产生了死琐。 lock.release() lock.release() 打印结果:会出现无法运行下去
import threading rLock = threading.RLock() #RLock对象 rLock.acquire() rLock.acquire() #在同一线程内,程序不会堵塞。 rLock.release() rLock.release()
这两种琐的主要区别是:RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。threading.Condition
可以把Condiftion理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。threadiong.Condition在内部维护一个琐对象(默认是RLock),可以在创建Condigtion对象的时候把琐对象作为参数传入。Condition也提供了acquire, release方法,其含义与琐的acquire, release方法一致,其实它只是简单的调用内部琐对象的对应的方法而已。Condition还提供了如下方法(特别要注意:这些方法只有在占用琐(acquire)之后才能调用,否则将会报RuntimeError异常。):
################这个方法作为补充######################
Condition.wait([timeout]):
wait方法释放内部所占用的琐,同时线程被挂起,直至接收到通知被唤醒或超时(如果提供了timeout参数的话)。当线程被唤醒并重新占有琐的时候,程序才会继续执行下去。
Condition.notify():
唤醒一个挂起的线程(如果存在挂起的线程)。注意:notify()方法不会释放所占用的琐。
Condition.notify_all()
Condition.notifyAll()
唤醒所有挂起的线程(如果存在挂起的线程)。注意:这些方法不会释放所占用的琐。
Semaphore(信号量)
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "wyd"
# Date: 2017/7/18
import threading
import time
semaphore= threading.Semaphore(5)#这里我们只允许同时运行5个线程
def fun():
if semaphore.acquire():#这里我们占用调用一把锁原来的可使用锁数-1
print(threading.currentThread().getName()+'get semaphore')#threading.currentThread().getName()查看使用的线程打印结果
time.sleep(0.5)
semaphore.release()
for i in range(20):#这里我们开启20个线程
t1=threading.Thread(target=fun)
t1.start()
=======================================打印的结果=============
Thread-1get semaphore
Thread-2get semaphore
Thread-3get semaphore
Thread-4get semaphore
Thread-5get semaphore