Alternative of threading.Timer?
<p>I have a producer-consumer pattern Queue, it consumes incoming events and schedule qualified events sending out in 5 seconds. I am using <code>threading.Timer()</code><a href="https://docs.python.org/2/library/sched.html" rel="nofollow">python document</a>to do it and everything was working fine.</p>

<p>Recently, I was requested to change the scheduled time from 5 second to 30 minutes, and <code>threading.Timer()</code> crashes my script because previously the threads objects are created and are released very soon(only last 5 sec) but now it has to keep alive for 30 minutes.</p>

<p>Here's the code:</p>

<pre><code> if scheduled_time and out_event:
     threading.Timer(scheduled_time, self.send_out_event, (socket_connection, received_event, out_event,)).start()  # schedule event send out
</code></pre>

<p>Can somesone shed some light on this? How can I solve this problem or is there any alternative for <code>threading.Timer()</code>?</p>

<p>Thanks for @dano 's comment about the 3rd party modules! Based on my work requirement, I didn't install them on the server. </p>

<p>Instead of using <code>threading.Timer()</code>, I choose to use a Redis based Delay Queue, I found some helpful source online: <a href="http://www.saltycrane.com/blog/2011/11/unique-python-redis-based-queue-delay/" rel="nofollow">A unique Python redis-based queue with delay</a>. It solved my issue.</p>

<p>Briefly, the author creates a sorted set in redis and give it a name, <code>add()</code> would appends new data into the sorted set. Every time it pops at most <strong>one</strong> element from the sorted set based upon the epoc-time score, the element which holds qualified minimum score would be pop out(<strong>Not remove from redis</strong>)</p>

<pre><code>def add(self, received_event, delay_queue_name="delay_queue", delay=config.SECOND_RETRY_DELAY):
    try:
        score = int(time.time()) + delay
        self.__client.zadd(delay_queue_name, score, received_event)
        self.__logger.debug("added {0} to delay queue, delay time:{1}".format(received_event, delay))
    except Exception as e:
        self.__logger.error("error: {0}".format(e))


def pop(self, delay_queue_name="delay_queue"):
    min_score, max_score, element = 0, int(time.time()), None

    try:
        result = self.__client.zrangebyscore(delay_queue_name, min_score, max_score, start=0, num=1, withscores=False)
    except Exception as e:
        self.__logger.error("failed query from redis:{0}".format(e))
        return None

    if result and len(result) == 1:
        element = result[0]
        self.__logger.debug("poped {0} from delay queue".format(element))
    else:
        self.__logger.debug("no qualified element")
    return element

def remove(self, element, delay_queue_name="delay_queue"):
    self.__client.zrem(delay_queue_name, element)
</code></pre>

<p><code>self.__client</code> is a Redis client instance, <code>redis.StrictRedis(host=rhost,port=rport, db=rindex)</code>. </p>

<p><strong>The difference between the online source with mine is that I switched <code>zadd()</code> parameters. The order of <code>score</code> and <code>data</code> are switched. Below is docs of</strong> <code>zadd()</code> </p>

<p>Here's the python redis doc:</p>

<pre><code># SORTED SET COMMANDS
def zadd(self, name, *args, **kwargs):
    """
    Set any number of score, element-name pairs to the key ``name``. Pairs
    can be specified in two ways:

    As *args, in the form of: score1, name1, score2, name2, ...
    or as **kwargs, in the form of: name1=score1, name2=score2, ...

    The following example would add four values to the 'my-key' key:
    redis.zadd('my-key', 1.1, 'name1', 2.2, 'name2', name3=3.3, name4=4.4)
    """
    pieces = []
    if args:
        if len(args) % 2 != 0:
            raise RedisError("ZADD requires an equal number of "
                             "values and scores")
        pieces.extend(args)
    for pair in iteritems(kwargs):
        pieces.append(pair[1])
        pieces.append(pair[0])
    return self.execute_command('ZADD', name, *pieces)
</code></pre>

python python-2.7 timer python-multithreading