再谈Python多线程--threading各类锁

来源:互联网 发布:什么是js面向对象编程 编辑:程序博客网 时间:2024/06/08 15:02
使用多线程的好处是提高执行效率,但同时带来了数据同步的问题。即多个线程同时对一个对象进行操作时,可能会出现资源冲突的问题;在不加锁的情况下,代码可能并未像我们想向的那样工作。举个栗子:
import threadingn = 2max_n = 10000x = 0def 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: 1Thread-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 threadingn = 2max_n = 10000x = 0lock = 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 threadingn = 2max_n = 10x = 0lock = 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:        returnfor 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等方法。



原创粉丝点击