锁与信号量
目录
由于多线程对资源的抢占顺序不同,可能会产生冲突,通过添加线程锁来对共有资源进行控制。
1 import atexit 2 from random import randrange 3 from threading import Thread, Lock, current_thread # or currentThread 4 from time import ctime, sleep 5 6 7 # Use set to record the running thread 8 # If we print set directly, it will shows set([a, b, c]) 9 # So we reload the __str__ function to show it more clean(shows a, b, c as we want) 10 class CleanOutputSet(set): 11 def __str__(self): 12 return ', '.join(x for x in self) 13 14 # randrange(2, 5) will generate a num in range(2, 5) 15 # for x in range(randrange(3, 7)) will do "for" loop 3-7 times 16 # Below code generate a list which contains 3-6 numbers in range 2-4 17 # If not list(), it will return a generator, and it will be null after one time iteration 18 loops = list((randrange(2, 5) for x in range(randrange(3, 7)))) 19 20 remaining = CleanOutputSet() 21 lock = Lock() 22 23 def loop_without_lock(nsec): 24 myname = current_thread().name 25 remaining.add(myname) 26 print('[%s] Start %s' % (ctime(), myname)) 27 sleep(nsec) 28 remaining.remove(myname) 29 print('[%s] Completed %s (%d secs)' % (ctime(), myname, nsec)) 30 # Note: remaining or 'NONE', will return 'NONE' if remaining is Null 31 # Null including: None, False, (), [], {}, 0 32 print(' (remaining: %s)' % (remaining or 'NONE')) 33 34 def loop_with_lock(nsec): 35 myname = current_thread().name 36 # When we need to modifiy public resource, acquire lock to block other threads 37 # Lock acquire and release can use 'with lock' to simplify 38 lock.acquire() 39 remaining.add(myname) 40 print('[%s] Start %s' % (ctime(), myname)) 41 # After using resource, release lock for other threads to use 42 lock.release() 43 sleep(nsec) 44 45 lock.acquire() 46 remaining.remove(myname) 47 print('[%s] Completed %s (%d secs)' % (ctime(), myname, nsec)) 48 print(' (remaining: %s)' % (remaining or 'NONE')) 49 lock.release() 50 51 def _main(): 52 print('-----Below threads without lock-----') 53 threads = [] 54 for pause in loops: 55 threads.append(Thread(target=loop_without_lock, args=(pause, ))) 56 for t in threads: 57 t.start() 58 for t in threads: 59 t.join() 60 print('-----Below threads with lock-----') 61 threads = [] 62 for pause in loops: 63 threads.append(Thread(target=loop_with_lock, args=(pause, ))) 64 for t in threads: 65 t.start() 66 for t in threads: 67 t.join() 68 69 # This is an exit function, when script exit, this function will be called 70 # You can use atexit.register(_atexit) to replace @atexit.register 71 # The function name '_atexit' can be change to others 72 @atexit.register 73 def _atexit(): 74 print('All DONE at:', ctime()) 75 76 if __name__ == '__main__': 77 _main()
第 1-4 行,首先导入需要的模块,atexit用于设置退出脚本时的处理函数,random用于产生随机数来增加线程的不确定性。
第 7- 12 行,定义一个新的集合类,用于输出当前运行线程的集合,新的集合类CleanOutputSet重载了__str__方法,使集合的显示由set([a, b, c])变为我们想要的a, b, c形式。
第 14-21行,利用随机数模块,产生一个生成器,包含3-6个大小在2-4之间的随机数,为了后续重复使用,此处对生成器进行list操作,否则生成器在迭代一次之后将变为空,无法复用。同时对全局锁和集合输出类进行实例化。
第 23-32 行,定义一个不加锁的线程函数,该函数会在进入时向集合添加线程名,sleep相应时间后,移除线程名,同时显示集合(共有资源)内剩余的线程名。
第 34-49 行,定义一个加锁的线程函数,该函数会在进入时获取线程锁,之后再向集合添加线程名,添加完成后释放线程锁,sleep相应时间后,获取线程锁,移除线程名,同时显示集合(共有资源)内剩余的线程名,最后释放线程锁。
第 51-67 行,主函数中分别对加锁和不加锁的两种线程方式进行调用,并利用join()方法挂起线程以区分开两种方式的运行。
第 69-77 行,利用atexit.register函数/@register装饰器定义脚本退出函数。
最后输出结果
-----Below threads without lock----- [Tue Aug 1 11:01:57 2017] Start Thread-1 [Tue Aug 1 11:01:57 2017] Start Thread-2[Tue Aug 1 11:01:57 2017] Start Thread-3 [Tue Aug 1 11:01:57 2017] Start Thread-4 [Tue Aug 1 11:01:59 2017] Completed Thread-3 (2 secs) (remaining: Thread-1, Thread-4, Thread-2) [Tue Aug 1 11:02:00 2017] Completed Thread-1 (3 secs) (remaining: Thread-4, Thread-2) [Tue Aug 1 11:02:01 2017] Completed Thread-2 (4 secs)[Tue Aug 1 11:02:01 2017] Completed Thread-4 (4 secs) (remaining: NONE) (remaining: NONE) -----Below threads with lock----- [Tue Aug 1 11:02:01 2017] Start Thread-5 [Tue Aug 1 11:02:01 2017] Start Thread-6 [Tue Aug 1 11:02:01 2017] Start Thread-7 [Tue Aug 1 11:02:01 2017] Start Thread-8 [Tue Aug 1 11:02:03 2017] Completed Thread-7 (2 secs) (remaining: Thread-8, Thread-6, Thread-5) [Tue Aug 1 11:02:04 2017] Completed Thread-5 (3 secs) (remaining: Thread-8, Thread-6) [Tue Aug 1 11:02:05 2017] Completed Thread-6 (4 secs) (remaining: Thread-8) [Tue Aug 1 11:02:05 2017] Completed Thread-8 (4 secs) (remaining: NONE)