1

I am new to SimPy and need help to figure out how to do this:

There are multiple foo processes running and a monitor process. At every time step, is there a way to ensure that the monitor process runs only after the other foo processes have finished running for that time step?

I attempt to do this in my example below by listening for changes to any process's is_alive property, then run the actual monitor logic after no more changes are detected.

However, from the output shown here, you can see that the string

1 : Doing something after all other processes....

appeared before

1 : a has finished after 1 steps

so monitor process did not manage to run its logic after all the other processes at environment step #1

Output

0 : Unfinished processes= [<Process(foo) object at 0x7f820b3df7f0>, <Process(foo) object at 0x7f820aae4790>, <Process(foo) object at 0x7f820aad2490>]
0 : Doing something after all other processes....
1 : Unfinished processes= [<Process(foo) object at 0x7f820b3df7f0>, <Process(foo) object at 0x7f820aae4790>, <Process(foo) object at 0x7f820aad2490>]
1 : Doing something after all other processes....
1 : a has finished after 1 steps
2 : Unfinished processes= [<Process(foo) object at 0x7f820aae4790>, <Process(foo) object at 0x7f820aad2490>]
2 : Doing something after all other processes....
3 : Unfinished processes= [<Process(foo) object at 0x7f820aae4790>, <Process(foo) object at 0x7f820aad2490>]
3 : Doing something after all other processes....
4 : Unfinished processes= [<Process(foo) object at 0x7f820aae4790>, <Process(foo) object at 0x7f820aad2490>]
4 : Doing something after all other processes....
5 : b has finished after 5 steps
5 : c has finished after 5 steps
5 : Doing something after all other processes....

My Simpy code

import simpy


def foo(env, id, delay):
    yield env.timeout(delay)
    print(f"{env.now} : {id} has finished after {delay} steps")


def monitor(env):

    while True:
        prev_unfinished_processes = []

        while True:
            unfinished_processes = [p for p in processes if p.is_alive]
            if set(unfinished_processes) != set(prev_unfinished_processes):
                print(env.now, ": Unfinished processes=", unfinished_processes)
            else:
                print(env.now, ": Doing something after all other processes....")
                break

            prev_unfinished_processes = unfinished_processes

        yield env.timeout(1)


env = simpy.Environment()
env.process(monitor(env))
processes = [
    env.process(foo(env, "a", 1)),
    env.process(foo(env, "b", 5)),
    env.process(foo(env, "c", 5)),
]
env.run(until=6)

Is there a better way to achieve this? Thanks

gameveloster
  • 901
  • 1
  • 6
  • 18

1 Answers1

0

One issue is that all the processes scheduled for a tick, fires one at a time, but the order each process fires is random. So you cannot count on your monitor running last after the other processes have finished.

At the end of each process I launched a process to check if all the processes for that tick are done. Since the check process is started after the running process, it will not start until the running process finishes. Since all the processes launch the check, every process gets checked, including the last one. I know a process is ready to be processed when triggered is true, and I know the process has completed when the processed is true.

import simpy

def foo(env, id, delay):
    yield env.timeout(delay)
    print(f"{env.now} : {id} has finished after {delay} steps")
    env.process(monitor2(env))

def monitor2(env):
    """
    Counts how many processes have been triggered (ready to be processed)
    and how many processes have been processed (completed)
    If all the processes have been triggered and processed then all
    the event processing is done
    """
    
    # need this to make it a proccess
    # and so it gets scheduled for the same tick
    yield env.timeout(0)

    trigCnt = 0
    procCnt = 0

    for p in processes:
        if p.triggered:
            trigCnt += 1
        if p.processed:
            procCnt += 1

    if trigCnt + procCnt > 0:
        if trigCnt == procCnt:
            print(env.now, "all proc done")

env = simpy.Environment()

processes = [
    env.process(foo(env, "a", 1)),
    env.process(foo(env, "b", 5)),
    env.process(foo(env, "c", 5)),
]
env.run(until=6)
Michael
  • 1,671
  • 2
  • 4
  • 8