Why this question is different from Python Lock always re-acquired by the same thread.
It may look similar because both questions use 2 threads. That question specifically requires each thread to execute once before handing over, creating an alternating foo,bar,foo,bar print pattern. It is looking for specific implementations to achieve that goal.
This question is more open. It is not looking for a specific pattern, but an explanation of what I see, and direction on what variables matter (to tweak) to study this phenomena.
import threading
import time
lock = threading.Lock()
def high_priority():
while True:
with lock:
print("High priority thread acquired lock")
time.sleep(0.2)
def low_priority():
while True:
with lock:
print("Low priority thread acquired lock") # May never happen!
time.sleep(0.1)
t1 = threading.Thread(target=high_priority, daemon=True)
t2 = threading.Thread(target=low_priority, daemon=True)
t1.start()
t2.start()
time.sleep(1)
I ran the code above 23 times before seeing low_priority get the lock.
My aim is to understand what determines
- when within the 1 second sleep of main script low_priority thread gets the lock
- how often low_priority thread gets the lock
However the experiments seem to require a long time so i'm looking for some theory first.
Last 3 runs' output out of 23:
➜ deadlock python deadlock_priority.py
High priority thread acquired lock
High priority thread acquired lock
High priority thread acquired lock
High priority thread acquired lock
High priority thread acquired lock
➜ deadlock python deadlock_priority.py
High priority thread acquired lock
High priority thread acquired lock
High priority thread acquired lock
High priority thread acquired lock
High priority thread acquired lock
➜ deadlock python deadlock_priority.py
High priority thread acquired lock
High priority thread acquired lock
High priority thread acquired lock
Low priority thread acquired lock
Low priority thread acquired lock
Low priority thread acquired lock
Low priority thread acquired lock
Is priority determined by order of start method, or by length of sleep in the threads?
t1.start()
t2.start()
I tried to sleep less on low_priority in hopes of giving it higher chance to take over, is this wishful thinking? I suspect time.sleep
settings inside the lock context manager in both threads have no impact.
Is there any way to make this experiment more reproducible? (meaning in a certain number of runs, i can see low_priority get the lock.
What other parameters are interesting to tweak or add to the code to understand this or related issues better?
I just wrote the code to study thread competition with no use case in mind, but intuition tells me there is some use. Can anyone explain if such patterns appear in meaningful code?
I decided to randomly add sleeps outside the lock context manager and somehow low_priority gets the lock a lot more often?
def high_priority():
while True:
time.sleep(0.0001)
with lock:
print("High priority thread acquired lock")
time.sleep(0.2)
def low_priority():
while True:
time.sleep(0.0001)
with lock:
print("Low priority thread acquired lock")
time.sleep(0.1)
Output of 1st 3 runs:
➜ deadlock python deadlock_sleep.py
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
➜ deadlock python deadlock_sleep.py
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
➜ deadlock python deadlock_sleep.py
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock
Low priority thread acquired lock
High priority thread acquired lock