6

I'm very new to python development, I need to call a function every x seconds.
So I'm trying to use a timer for that, something like:

def start_working_interval():
    def timer_tick():
        do_some_work() // need to be called on the main thread

        timer = threading.Timer(10.0, timer_tick)
        timer.start()

    timer = threading.Timer(10.0, timer_tick)
    timer.start()  

the do_some_work() method need to be called on the main thread, and I think using the timer causing it to execute on different thread.

so my question is, how can I call this method on the main thread?

Mario
  • 2,431
  • 6
  • 27
  • 34

2 Answers2

0

I'm now sure what you trying to achive but i played with your code and did this:

import threading
import datetime


def do_some_work():
    print datetime.datetime.now()

def start_working_interval():
    def timer_tick():
        do_some_work()  

        timer = threading.Timer(10.0, timer_tick)
        timer.start()
    timer_tick()

start_working_interval()

So basically what i did was to set the Time inside the timer_tick() so it will call it-self after 10 sec and so on, but i removed the second timer.

Or Duan
  • 13,142
  • 6
  • 60
  • 65
  • 1
    ya but how do i make sure that do_some_work will be called on the main thread? – Mario Nov 16 '14 at 16:03
  • @Mario - in this example, it won't. You'll have to enlighten us as to what your main thread does so that we can help you. It may also help for you to explain why it needs to be run on the main thread. – Cel Skeggs Nov 16 '14 at 20:32
-1

I needed to do this too, here's what I did:

import time

MAXBLOCKINGSECONDS=5 #maximum time that a new task will have to wait before it's presence in the queue gets noticed.
class repeater:
    repeatergroup=[]                        #our only static data member it holds the current list of the repeaters that need to be serviced
    def __init__(self,callback,interval):
        self.callback=callback              
        self.interval=abs(interval)         #because negative makes no sense, probably assert would be better.
        self.reset()
        self.processing=False
    def reset(self):
        self.nextevent=time.time()+self.interval
    def whennext(self):
        return self.nextevent-time.time()   #time until next event
    def service(self):
        if time.time()>=self.nextevent:
            if self.processing=True:        #or however you want to be re-entrant safe or thread safe
                return 0
            self.processing==True
            self.callback(self)             #just stuff all your args into the class and pull them back out?

            #use this calculation if you don't want slew
            self.nextevent+=self.interval
            #reuse this calculation if you do want slew/don't want backlog
            #self.reset()
            #or put it just before the callback
            self.processing=False
            return 1
        return 0

    #this the transition code between class and classgroup
    #I had these three as a property getter and setter but it was behaving badly/oddly
    def isenabled(self):
        return (self in self.repeatergroup)
    def start(self):
        if not (self in self.repeatergroup):
            self.repeatergroup.append(self)
            #another logical place to call reset if you don't want backlog:
            #self.reset()
    def stop(self):
        if (self in self.repeatergroup):
            self.repeatergroup.remove(self)

    #group calls in c++ I'd make these static
    def serviceall(self):                   #the VB hacker in me wants to name this doevents(), the c hacker in me wants to name this probe
        ret=0
        for r in self.repeatergroup:
            ret+=r.service()
        return ret
    def minwhennext(self,max):              #this should probably be hidden
        ret=max
        for r in self.repeatergroup:
            ret=min(ret,r.whennext())
        return ret
    def sleep(self,seconds):
        if not isinstance(threading.current_thread(), threading._MainThread): #if we're not on the main thread, don't process handlers, just sleep.
            time.sleep(seconds)
            return
        endtime=time.time()+seconds         #record when caller wants control back
        while time.time()<=endtime:         #spin until then
            while self.serviceall()>0:      #service each member of the group until none need service
                if (time.time()>=endtime):
                    return                  #break out of service loop if caller needs control back already
            #done with servicing for a while, yield control to os until we have
            #another repeater to service or it's time to return control to the caller
            minsleeptime=min(endtime-time.time(),MAXBLOCKINGPERIOD) #smaller of caller's requested blocking time, and our sanity number (1 min might be find for some systems, 5 seconds is good for some systems, 0.25 to 0.03 might be better if there could be video refresh code waiting, 0.15-0.3 seems a common range for software denouncing of hardware buttons.
            minsleeptime=self.minwhennext(minsleeptime)
            time.sleep(max(0,minsleeptime))
###################################################################
#  and now some demo code:

def handler1(repeater):
    print("latency is currently {0:0.7}".format(time.time()-repeater.nextevent))
    repeater.count+=repeater.interval
    print("Seconds: {0}".format(repeater.count))

def handler2(repeater):                     #or self if you prefer
    print("Timed message is: {0}".format(repeater.message))
    if repeater.other.isenabled():
        repeater.other.stop()
    else:
        repeater.other.start()
    repeater.interval+=1

def demo_main():
    counter=repeater(handler1,1)
    counter.count=0                         #I'm still new enough to python
    counter.start()
    greeter=repeater(handler2,2)
    greeter.message="Hello world."          #that this feels like cheating
    greeter.other=counter                   #but it simplifies everything.
    greeter.start()
    print ("Currently {0} repeaters in service group.".format(len(repeater.repeatergroup)))
    print("About to yield control for a while")
    greeter.sleep(10)

    print("Got control back, going to do some processing")
    time.sleep(5)

    print("About to yield control for a while")      
    counter.sleep(20) #you can use any repeater to access sleep() but
    #it will only service those currently enabled.

    #notice how it gets behind but tries to catch up, we could add repeater.reset()
    #at the beginning of a handler to make it ignore missed events, or at the
    #end to let the timing slide, depending on what kind of processing we're doing
    #and what sort of sensitivity there is to time.

    #now just replace all your main thread's calls to time.sleep() with calls to mycounter.sleep()
    #now just add a repeater.sleep(.01) or a while repeater.serviceall(): pass to any loop that will take too long.
demo_main()

There's a couple of odd things left to consider: Would it be better to sort handlers that you'd prefer to run on main thread from handlers that you don't care? I later went on to add a threadingstyle property, which depending on it's value would run on main thread only, on either main thread or a shared/group thread, or stand alone on it's own thread. That way longer or more time-sensitive tasks, could run without causing the other threads to be as slowed down, or closer to their scheduled time. I wonder whether, depending on the implementation details of threading: is my 'if not main thread: time.sleep(seconds); return' effectively make it sufficiently more likely to be the main thread's turn, and I shouldn't worry about the difference. (It seems like adding our MAXBLOCKINGPERIOD as the 3rd arg to the sched library could fix it's notorious issue of not servicing new events after older longer in the future events have already hit the front of the queue.)

hbregalad
  • 84
  • 3