使用多线程的好处是提高执行效率,但同时带来了数据同步的问题。即多个线程同时对一个对象进行操作时,可能会出现资源冲突的问题;在不加锁的情况下,代码可能并未像我们想向的那样工作。举个栗子:
为了保证多线程对共享资源的访问顺序,一般会引入锁机制。有了锁的加入,当子线程在操作共享资源时会先对其进行锁定,之后再进行操作;而此时其它的线程是不能对该共享资源进行操作的。这样就保证了同一个时间只有一个线程在操作共享资源。在Python中多线程锁有几种实现形式:
首先, Lock是我们最常用的锁,它主要提供2个操作方法:acquire【申请锁】、release【释放锁】。
RLock是Recursion Lock的简称,即递归锁。它主要用于递归操作中,比如:递归函数。它与Lock的区别在于RLock在同一个线程内可以重复申请,所以它可以被用于递归操作中。
Condition是一把有条件的锁。本质上它是对Lock/RLock等基本锁对象的封装,在实例Condition对象时需要传入需要的基本锁。而在使用上Condition除了可以使用acquire和release方法,还有wait、notify、notifyAll等方法。
import threading
n = 2
max_n = 10000
x = 0
def countup(n):
global x
for i in range(n):
x += 1
print '%s: %s\r\n' % (threading.currentThread().getName(), x)
for i in range(n):
t = threading.Thread(target=countup, args=(max_n,))
t.start()
执行结果:
Thread-1: 1
Thread-2: 2
...
Thread-2: 19912
按照期望最后应该打印到20000才结束,而实际上只打印了19912次,并且每次执行的结果很可能是不一样的。
为了保证多线程对共享资源的访问顺序,一般会引入锁机制。有了锁的加入,当子线程在操作共享资源时会先对其进行锁定,之后再进行操作;而此时其它的线程是不能对该共享资源进行操作的。这样就保证了同一个时间只有一个线程在操作共享资源。在Python中多线程锁有几种实现形式:
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore
- threading.Event
- threading.Timer
- threading.Barrier
首先, Lock是我们最常用的锁,它主要提供2个操作方法:acquire【申请锁】、release【释放锁】。
import threading
n = 2
max_n = 10000
x = 0
lock = threading.Lock()
def countup(n):
global x
for i in range(n):
lock.acquire()
x += 1
lock.release()
print '%s: %s\r\n' % (threading.currentThread().getName(), x)
for i in range(n):
t = threading.Thread(target=countup, args=(max_n,))
t.start()
这次在加了锁之后,程序终于可以正常工作了。因为我们在对x变量进行操作的前进行了加锁操作,保证了对x的修改操作是一个原子操作(不可被分割的操作)。
RLock是Recursion Lock的简称,即递归锁。它主要用于递归操作中,比如:递归函数。它与Lock的区别在于RLock在同一个线程内可以重复申请,所以它可以被用于递归操作中。
import threading
n = 2
max_n = 10
x = 0
lock = threading.RLock()
def countup(m):
global x
if m > 0:
lock.acquire()
x += 1
countup(m - 1)
lock.release()
print '%s: %s\r\n' % (threading.currentThread().getName(), x)
else:
return
for i in range(n):
t = threading.Thread(target=countup, args=(max_n,))
t.start()
上面我们使用递归替代了for循环,并且递归调用是在获取到锁的过程中。如果你把上面的RLock替换成Lock,那么它将会被自己组塞住。当然你也可以使用尾递归的写法,那样还是可以使用Lock模块;这里只是说明RLock的应用场景。
Condition是一把有条件的锁。本质上它是对Lock/RLock等基本锁对象的封装,在实例Condition对象时需要传入需要的基本锁。而在使用上Condition除了可以使用acquire和release方法,还有wait、notify、notifyAll等方法。