锁
python的内置数据结构比如列表和字典等是线程安全的,但是简单数据类型比如整数和浮点数则不是线程安全的,要这些简单数据类型的通过操作,就需要使用锁。
#!/usr/bin/env python3
# coding=utf-8
import threading
shared_resource_with_lock = 0
shared_resource_with_no_lock = 0
COUNT = 100000
shared_resource_lock = threading.Lock()
####LOCK MANAGEMENT##
def increment_with_lock():
global shared_resource_with_lock
for i in range(COUNT):
shared_resource_lock.acquire()
shared_resource_with_lock += 1
shared_resource_lock.release()
def decrement_with_lock():
global shared_resource_with_lock
for i in range(COUNT):
shared_resource_lock.acquire()
shared_resource_with_lock -= 1
shared_resource_lock.release()
####NO LOCK MANAGEMENT ##
def increment_without_lock():
global shared_resource_with_no_lock
for i in range(COUNT):
shared_resource_with_no_lock += 1
def decrement_without_lock():
global shared_resource_with_no_lock
for i in range(COUNT):
shared_resource_with_no_lock -= 1
####the Main program
if __name__ == "__main__":
t1 = threading.Thread(target = increment_with_lock)
t2 = threading.Thread(target = decrement_with_lock)
t3 = threading.Thread(target = increment_without_lock)
t4 = threading.Thread(target = decrement_without_lock)
t1.start()
t2.start()
t3.start()
t4.start()
t1.join()
t2.join()
t3.join()
t4.join()
print ("the value of shared variable with lock management is %s"\
%shared_resource_with_lock)
print ("the value of shared variable with race condition is %s"\
%shared_resource_with_no_lock)
执行结果:
$ ./threading_lock.py
the value of shared variable with lock management is 0
the value of shared variable with race condition is 0
又如:
import random
import threading
import time
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
class Counter(object):
def __init__(self, start=0):
self.lock = threading.Lock()
self.value = start
def increment(self):
logging.debug(time.ctime(time.time()))
logging.debug('Waiting for lock')
self.lock.acquire()
try:
pause = random.randint(1,3)
logging.debug(time.ctime(time.time()))
logging.debug('Acquired lock')
self.value = self.value + 1
logging.debug('lock {0} seconds'.format(pause))
time.sleep(pause)
finally:
self.lock.release()
def worker(c):
for i in range(2):
pause = random.randint(1,3)
logging.debug(time.ctime(time.time()))
logging.debug('Sleeping %0.02f', pause)
time.sleep(pause)
c.increment()
logging.debug('Done')
counter = Counter()
for i in range(2):
t = threading.Thread(target=worker, args=(counter,))
t.start()
logging.debug('Waiting for worker threads')
main_thread = threading.currentThread()
for t in threading.enumerate():
if t is not main_thread:
t.join()
logging.debug('Counter: %d', counter.value)
执行结果:
$ python threading_lock.py
(Thread-1 ) Tue Sep 15 15:49:18 2015
(Thread-1 ) Sleeping 3.00
(Thread-2 ) Tue Sep 15 15:49:18 2015
(MainThread) Waiting for worker threads
(Thread-2 ) Sleeping 2.00
(Thread-2 ) Tue Sep 15 15:49:20 2015
(Thread-2 ) Waiting for lock
(Thread-2 ) Tue Sep 15 15:49:20 2015
(Thread-2 ) Acquired lock
(Thread-2 ) lock 2 seconds
(Thread-1 ) Tue Sep 15 15:49:21 2015
(Thread-1 ) Waiting for lock
(Thread-2 ) Tue Sep 15 15:49:22 2015
(Thread-1 ) Tue Sep 15 15:49:22 2015
(Thread-2 ) Sleeping 2.00
(Thread-1 ) Acquired lock
(Thread-1 ) lock 1 seconds
(Thread-1 ) Tue Sep 15 15:49:23 2015
(Thread-1 ) Sleeping 2.00
(Thread-2 ) Tue Sep 15 15:49:24 2015
(Thread-2 ) Waiting for lock
(Thread-2 ) Tue Sep 15 15:49:24 2015
(Thread-2 ) Acquired lock
(Thread-2 ) lock 1 seconds
(Thread-1 ) Tue Sep 15 15:49:25 2015
(Thread-1 ) Waiting for lock
(Thread-1 ) Tue Sep 15 15:49:25 2015
(Thread-1 ) Acquired lock
(Thread-1 ) lock 2 seconds
(Thread-2 ) Done
(Thread-1 ) Done
(MainThread) Counter: 4
acquire()中传入False值,可以检查是否获得了锁。比如:
import logging
import threading
import time
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
def lock_holder(lock):
logging.debug('Starting')
while True:
lock.acquire()
try:
logging.debug('Holding')
time.sleep(0.5)
finally:
logging.debug('Not holding')
lock.release()
time.sleep(0.5)
return
def worker(lock):
logging.debug('Starting')
num_tries = 0
num_acquires = 0
while num_acquires < 3:
time.sleep(0.5)
logging.debug('Trying to acquire')
have_it = lock.acquire(0)
try:
num_tries += 1
if have_it:
logging.debug('Iteration %d: Acquired',
num_tries)
num_acquires += 1
else:
logging.debug('Iteration %d: Not acquired',
num_tries)
finally:
if have_it:
lock.release()
logging.debug('Done after %d iterations', num_tries)
lock = threading.Lock()
holder = threading.Thread(target=lock_holder,
args=(lock,),
name='LockHolder')
holder.setDaemon(True)
holder.start()
worker = threading.Thread(target=worker,
args=(lock,),
name='Worker')
worker.start()
执行结果:
$ python threading_lock_noblock.py
(LockHolder) Starting
(LockHolder) Holding
(Worker ) Starting
(LockHolder) Not holding
(Worker ) Trying to acquire
(Worker ) Iteration 1: Acquired
(LockHolder) Holding
(Worker ) Trying to acquire
(Worker ) Iteration 2: Not acquired
(LockHolder) Not holding
(Worker ) Trying to acquire
(Worker ) Iteration 3: Acquired
(LockHolder) Holding
(Worker ) Trying to acquire
(Worker ) Iteration 4: Not acquired
(LockHolder) Not holding
(Worker ) Trying to acquire
(Worker ) Iteration 5: Acquired
(Worker ) Done after 5 iterations
线程安全锁
threading.RLock()
返回可重入锁对象。重入锁必须由获得它的线程释放。一旦线程获得了重入锁,同一线程可不阻塞地再次获得,获取之后必须释放。
通常一个线程只能获取一次锁:
import threading
lock = threading.Lock()
print 'First try :', lock.acquire()
print 'Second try:', lock.acquire(0)
执行结果:
$ python threading_lock_reacquire.py
First try : True
Second try: False
使用RLock可以获取多次锁:
import threading
lock = threading.RLock()
print 'First try :', lock.acquire()
print 'Second try:', lock.acquire(0)
执行结果:
python threading_rlock.py
First try : True
Second try: 1
再来看一个例子:
#!/usr/bin/env python3
# coding=utf-8
import threading
import time
class Box(object):
lock = threading.RLock()
def __init__(self):
self.total_items = 0
def execute(self,n):
Box.lock.acquire()
self.total_items += n
Box.lock.release()
def add(self):
Box.lock.acquire()
self.execute(1)
Box.lock.release()
def remove(self):
Box.lock.acquire()
self.execute(-1)
Box.lock.release()
## These two functions run n in separate
## threads and call the Box's methods
def adder(box,items):
while items > 0:
print ("adding 1 item in the box\n")
box.add()
time.sleep(5)
items -= 1
def remover(box,items):
while items > 0:
print ("removing 1 item in the box")
box.remove()
time.sleep(5)
items -= 1
## the main program build some
## threads and make sure it works
if __name__ == "__main__":
items = 5
print ("putting %s items in the box " % items)
box = Box()
t1 = threading.Thread(target=adder,args=(box,items))
t2 = threading.Thread(target=remover,args=(box,items))
t1.start()
t2.start()
t1.join()
t2.join()
print ("%s items still remain in the box " % box.total_items)
执行结果:
$ python3 threading_rlock2.py
putting 5 items in the box
adding 1 item in the box
removing 1 item in the box
adding 1 item in the box
removing 1 item in the box
adding 1 item in the box
removing 1 item in the box
removing 1 item in the box
adding 1 item in the box
removing 1 item in the box
adding 1 item in the box
0 items still remain in the box