A lock turns a window into a section. Only one writer is inside at a time, so the read and the write can no longer be split apart by timing.
What you will see
1
About 2 min of reading
Quick check
Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.
Try It
Check that a lock closes the window.
1
Count exact.
import threading
lock = threading.Lock()
total = 0
for _ in range(5):
with lock:
total = total + 1
print(total)
Before you run it, say out loud what it will print. Then open the answer.
What it prints
5
2
Lock held once.
import threading
lock = threading.Lock()
with lock:
print("inside")
Before you run it, say out loud what it will print. Then open the answer.
What it prints
inside
3
Counter right.
import threading
lock = threading.Lock()
count = 0
def bump():
global count
with lock:
count = count + 1
for _ in range(4):
bump()
print(count)
Before you run it, say out loud what it will print. Then open the answer.
What it prints
4
4
Two locks shown.
import threading
left = threading.Lock()
right = threading.Lock()
total = 0
with left:
with right:
total = total + 1
print(total)
Before you run it, say out loud what it will print. Then open the answer.
What it prints
1
Put it in order
Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.
Data Flow
A writer takes the lock, the section runs alone, the lock is released, and the next writer is let in.
1take the lock
↓
2run the section alone
↓
3release it
↓
4the next writer waits
Line by line
Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.
import threadingsets nothing on screen
lock = threading.Lock()sets nothing on screen
total = 0sets nothing on screen
for _ in range(5):sets nothing on screen
with lock:sets nothing on screen
total = total + 1sets nothing on screen
print(total)5
Why this worksSame lock everywhere
A lock is a name for a section, not a rule. Every writer of that value has to take that same lock or the window is still open.
Short sections win
The cost of a lock is the time it is held. Do the slow work outside and keep only the shared read and write inside.
Examples
Guard the section
import threading
lock = threading.Lock()
total = 0defadd():
global total
for _ inrange(1000):
with lock:
total = total + 1
a = threading.Thread(target=add)
b = threading.Thread(target=add)
a.start()
b.start()
a.join()
b.join()
print(total)
The lock holds the read and the write together, so the count is always 2000.
A lock you forgot
import threading
lock = threading.Lock()
total = 0defadd():
global total
total = total + 1with lock:
passprint(total)
A lock that guards nothing is a lock that does nothing. Every writer has to take the same one.
Two different locks
import threading
left = threading.Lock()
right = threading.Lock()
total = 0with left:
with right:
total = total + 1print(total)
Two writers using two different locks never wait for each other. A lock only excludes who takes that same lock.
Hold it short
import threading
lock = threading.Lock()
shared = [0]
with lock:
shared[0] = shared[0] + 1
value = shared[0]
print(value)
Work outside the lock can overlap. Only the shared read and write need to sit inside.
A context, not a call
import threading
lock = threading.Lock()
with lock:
print("inside")
with releases on every exit, including an exception. A manual release can be skipped and the lock stays held.