问题的提出
上一节的例子中,每个线程相互独立,彼此之间没有任何关系。此刻假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,按照num举办一些处理惩罚,然后将num加1。很容易写出这样的代码:
# encoding: UTF-8 import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) num = num+1 msg = self.name+' set num to '+str(num) print msg num = 0 def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
可是运行功效是不正确的:
Thread-5 set num to 2
Thread-3 set num to 3
Thread-2 set num to 5
Thread-1 set num to 5
Thread-4 set num to 4
问题发生的原因就是没有节制多个线程对同一资源的会见,对数据造成粉碎,使得线程运行的功效不行预期。这种现象称为“线程不安详”。
互斥锁同步
上面的例子引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要举办同步节制。
线程同步可以或许担保多个线程安详会见竞争资源,最简朴的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要变动共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能变动;直到该线程释放资源,将资源的状态酿成“非锁定”,其他的线程才气再次锁定该资源。互斥锁担保了每次只有一个线程举办写入操纵,从而担保了多线程环境下数据的正确性。
threading模块中界说了Lock类,可以利便的处理惩罚锁定:
#建设锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()
个中,锁定要领acquire可以有一个超时时间的可选参数timeout。假如设定了timeout,则在超时后通过返回值可以判定是否获得了锁,从而可以举办一些其他的处理惩罚。
利用互斥锁实现上面的例子的代码如下:
import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) if mutex.acquire(1): num = num+1 msg = self.name+' set num to '+str(num) print msg mutex.release() num = 0 mutex = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
运行功效:
Thread-3 set num to 1
Thread-4 set num to 2
Thread-5 set num to 3
Thread-2 set num to 4
Thread-1 set num to 5
可以看到,插手互斥锁后,运行功效与预期相符。
同步阻塞
当一个线程挪用锁的acquire()要领得到锁时,锁就进入“locked”状态。每次只有一个线程可以得到锁。假如此时另一个线程试图得到这个锁,该线程就会变为“blocked”状态,称为“同步阻塞”(拜见多线程的根基观念)。
直到拥有锁的线程挪用锁的release()要领释放锁之后,锁进入“unlocked”状态。线程调治措施从处于同步阻塞状态的线程中选择一个来得到锁,并使得该线程进入运行(running)状态。
互斥锁最根基的内容就是这些,下一节将接头可重入锁(RLock)和死锁问题。