user3595632 user3595632 - 22 days ago 6
Python Question

Python: execution order in Threading

This is a code for testing

Threading
:

import threading
import time


episode = 0
lock = threading.Lock()

class Agent(threading.Thread):
def __init__(self, id):
threading.Thread.__init__(self)
self.id = id

def run(self):
global episode
while episode < 5:
with lock:
print(
"{} : episode will be changed {} -> {}".format(
self.id,
episode,
episode+1
)
)
episode += 1
print("{} : changed value -> {}".format(self.id, episode))
time.sleep(1)


if __name__ == "__main__":
agents = []
for i in range(3):
agents.append(Agent(i))

for agent in agents:
agent.start()


Result:

0 : episode will be changed 0 -> 1
0 : changed value -> 1
0 : episode will be changed 1 -> 2
0 : changed value -> 2
0 : episode will be changed 2 -> 3
0 : changed value -> 3
0 : episode will be changed 3 -> 4
0 : changed value -> 4
0 : episode will be changed 4 -> 5
0 : changed value -> 5
1 : episode will be changed 5 -> 6
1 : changed value -> 6
2 : episode will be changed 6 -> 7
2 : changed value -> 7


This is one of result that I had expected:

0 : episode will be changed 0 -> 1
0 : changed value -> 1
2 : episode will be changed 1 -> 2
2 : changed value -> 2
1 : episode will be changed 2 -> 3
1 : changed value -> 3
2 : episode will be changed 3 -> 4
2 : changed value -> 4
0 : episode will be changed 4 -> 5
0 : changed value -> 5
.
.


I can not understand why thread id=0 keep appearing consecutively in the first place... As I know of, the execution order of threads is random, right?

What's wrong in my code?

Answer Source

Thread 0 starts first, grabs the lock, and sleeps while holding the lock. There is a very short period of time between the sleep ending, the lock getting released as a result of exiting the with block, and the loop acquiring the lock again, so chances are excellent that thread 0 will get the lock again ... and again, and again, until episode < 5 is finally false.

Remove a level of indentation on your time.sleep(1) so it executes as part of the loop instead of as part of the with block. Then thread 0 will release the lock before starting its sleep, and other threads are almost certain to get the lock while thread 0 is sleeping (they have a whole second to do so then, instead of far less than an eyeblink).