3

I have a list of objects of a particular class where each object has a unix time attribute. I would like to execute a function

class Car:

    def __init__(self,  id, time):
       self.id = id
       self.time = time

objct = [Car(i, time_unix[i]) for i in range(10)]

where time_unix is a list of unix times. I would like to create a new thread that will run 'in parallel' to this code where it checks the current time. If the current time is equal to one of the object's unix_time, a new function called drive will be invoked.

So I would like to have a thread that is on constant alert for invoking a drive function since I might have other cars coming in with their respective time to drive.

bcsta
  • 1,963
  • 3
  • 22
  • 61
  • @Martijn Pieters The marked as duplicate link is about scheduling one specific event repeatedly at a fixed time interval (periodically). This question here is about individual per-object scheduling without a fixed general time interval and where the total amount of scheduled tasks is unspecified. – Darkonaut Nov 05 '18 at 14:56
  • @Darkonaut: yet the solution is the same, use `threading.Timer`. – Martijn Pieters Nov 05 '18 at 15:16
  • @Martijn Pieters Isn't the policy to decide this on the question if it's a dup? It's not clear which answer you think is _also_ a solution for this just by linking a question. – Darkonaut Nov 05 '18 at 15:19
  • @Darkonaut: I've added a few more links there. The policy is to point to a canonical that answers the problem. – Martijn Pieters Nov 05 '18 at 15:21

2 Answers2

3

You can use threading.Timer for scheduling execution of a target function in a separate thread after a specified time interval.

Timer Objects

This class represents an action that should be run only after a certain amount of time has passed — a timer. Timer is a subclass of Thread and as such also functions as an example of creating custom threads.

Timers are started, as with threads, by calling their start() method. The timer can be stopped (before its action has begun) by calling the cancel() method. The interval the timer will wait before executing its action may not be exactly the same as the interval specified by the user. docs

from threading import Timer, current_thread
from datetime import datetime


def drive(car):
    print(f'{datetime.now()} tid:{current_thread()} {car}: brumbrum')


class Car:
    def __init__(self,  id, time):
       self.id = id
       self.time = time


if __name__ == '__main__':

    N_CARS = 5

    time_unix = {k: v for k, v in zip(range(N_CARS), range(N_CARS))}
    cars = [Car(f'car_{i}', time_unix[i]) for i in range(N_CARS)]

    for car in cars:
        interval = car.time  # calculate delay in seconds for execution here
        t = Timer(interval=interval, function=drive, args=(car.id,))
        t.start()

Example Output:

2018-11-05 13:01:50.999886 tid:<Timer(Thread-2, started 139979005781760)> car_0: brumbrum
2018-11-05 13:01:52.000360 tid:<Timer(Thread-3, started 139978997389056)> car_1: brumbrum
2018-11-05 13:01:53.000452 tid:<Timer(Thread-4, started 139979005781760)> car_2: brumbrum
2018-11-05 13:01:54.000533 tid:<Timer(Thread-5, started 139978986825472)> car_3: brumbrum
2018-11-05 13:01:55.000625 tid:<Timer(Thread-6, started 139978978432768)> car_4: brumbrum

Process finished with exit code 0
Community
  • 1
  • 1
Darkonaut
  • 20,186
  • 7
  • 54
  • 65
  • this looks like what I need.Is it a good idea to put the t=Timer() t.start() code inside Car's constructor. so everytime a car is created the thread is incovked? – bcsta Nov 05 '18 at 12:30
  • @user7331538 Would be an option. You could also subclass `Timer` and name this class `Car`. I prefer not to bury the thread inside a normal class but subclassing threads directly to make the threaded character easier to spot. – Darkonaut Nov 05 '18 at 12:47
0

You don't need to check the current time every now and then. You can calculate how many seconds the thread needs to wait and call time.sleep.

Combined with this answer, you get:

from multiprocessing.dummy import Pool as ThreadPool
from time import sleep

pool = ThreadPool(4) 
cars = [('Car1', 3), ('Car2', 1), ('Car3', 2)]

def start_car(car, time_to_wait):
    sleep(time_to_wait)
    print("Starting %s after %d seconds" % (car, time_to_wait))

pool.starmap(start_car, cars)

It outputs:

Starting Car2 after 1 seconds
Starting Car3 after 2 seconds
Starting Car1 after 3 seconds
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
  • But what if new Cars are being added and I dont know before hand that I need 4 threads? That is why I asked for a thread that is constantly looking out for new incoming cars and their time to start driving. In other words what if cars list is changing with time? – bcsta Nov 05 '18 at 10:47