锁
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