user3595632
user3595632

Reputation: 5730

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?

Upvotes: 2

Views: 1866

Answers (2)

Sraw
Sraw

Reputation: 20206

You are just unlucky? I have tested your code without changing anything and got the following output:

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

Upvotes: 1

Tim Peters
Tim Peters

Reputation: 70582

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).

Upvotes: 2

Related Questions