复制代码 代码如下:
'''
created on 2012-9-8
@author: walfred
@module: thread.threadtest3
'''
import threading
import time
counter = 0
class mythread(threading.thread):
def __init__(self):
threading.thread.__init__(self)
def run(self):
global counter
time.sleep(1);
counter += 1
print i am %s, set counter:%s % (self.name, counter)
if __name__ == __main__:
for i in range(0, 200):
my_thread = mythread()
my_thread.start()
解决上面的问题,我们兴许会写出这样的代码,我们假设跑200个线程,但是这200个线程都会去访问counter这个公共资源,并对该资源进行处理(counter += 1),代码看起来就是这个样了,但是我们看下运行结果:
复制代码 代码如下:
i am thread-69, set counter:64
i am thread-73, set counter:66i am thread-74, set counter:67i am thread-75, set counter:68i am thread-76, set counter:69i am thread-78, set counter:70i am thread-77, set counter:71i am thread-58, set counter:72i am thread-60, set counter:73i am thread-62, set counter:74i am thread-66,set counter:75i am thread-70, set counter:76i am thread-72, set counter:77i am thread-79, set counter:78i am thread-71, set counter:78
打印结果我只贴了一部分,从中我们已经看出了这个全局资源(counter)被抢占的情况,问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。在开发过程中我们必须要避免这种情况,那怎么避免?这就用到了我们在综述中提到的互斥锁了。互斥锁概念
python编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。在python中我们使用threading模块提供的lock类。
我们对上面的程序进行整改,为此我们需要添加一个互斥锁变量mutex = threading.lock(),然后在争夺资源的时候之前我们会先抢占这把锁mutex.acquire(),对资源使用完成之后我们在释放这把锁mutex.release()。代码如下:
复制代码 代码如下:
'''
created on 2012-9-8
@author: walfred
@module: thread.threadtest4
'''
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()
同步阻塞
当一个线程调用lock对象的acquire()方法获得锁时,这把锁就进入“locked”状态。因为每次只有一个线程1可以获得锁,所以如果此时另一个线程2试图获得这个锁,该线程2就会变为“blo同步阻塞状态。直到拥有锁的线程1调用锁的release()方法释放锁之后,该锁进入“unlocked”状态。线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
进一步考虑
通过对公共资源使用互斥锁,这样就简单的到达了我们的目的,但是如果我们又遇到下面的情况:
遇到锁嵌套的情况该怎么办,这个嵌套是指当我一个线程在获取临界资源时,又需要再次获取;
如果有多个公共资源,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源;
上述这两种情况会直接造成程序挂起,即死锁,下面我们会谈死锁及可重入锁rlock。
