python的内置数据结构比如列表和字典等是线程安全的,但是简单数据类型比如整数和浮点数则不是线程安全的,要这些简单数据类型的通过操作,就需要使用锁。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#!/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) |
执行结果:
1
|
$ . /threading_lock .py |
1
2
|
the value of shared variable with lock management is 0 the value of shared variable with race condition is 0 |
又如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
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) |
执行结果:
1
|
$ python threading_lock.py |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
(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值,可以检查是否获得了锁。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
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() |
执行结果:
1
|
$ python threading_lock_noblock.py |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
(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 |
线程安全锁
1
|
threading.RLock() |
返回可重入锁对象。重入锁必须由获得它的线程释放。一旦线程获得了重入锁,同一线程可不阻塞地再次获得,获取之后必须释放。
通常一个线程只能获取一次锁:
1
2
3
4
5
6
|
import threading lock = threading.Lock() print 'First try :' , lock.acquire() print 'Second try:' , lock.acquire( 0 ) |
执行结果:
1
|
$ python threading_lock_reacquire.py |
1
2
|
First try : True Second try: False |
使用RLock可以获取多次锁:
1
2
3
4
|
import threading lock = threading.RLock() print 'First try :' , lock.acquire() print 'Second try:' , lock.acquire( 0 ) |
执行结果:
1
|
python threading_rlock.py |
1
2
|
First try : True Second try: 1 |
再来看一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#!/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) |
执行结果:
1
|
$ python3 threading_rlock2.py |
1
2
3
4
5
6
7
8
9
10
11
12
|
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 |