my code. it starts 2 threads with inner schedulers that print number every second
import threading
import time
from datetime import datetime
import schedule
_lock = threading.Lock()
def p(number):
_lock.acquire()
print(number, datetime.now())
_lock.release()
def f(number):
schedule.every(5).seconds.do(p, number)
while True:
schedule.run_pending()
time.sleep(1)
thread = threading.Thread(target=f, args=(1,))
thread2 = threading.Thread(target=f, args=(2,))
thread.start()
thread2.start()
expected output
1 2020-03-25 22:07:17.817528
2 2020-03-25 22:07:17.817528
1 2020-03-25 22:07:22.821887
2 2020-03-25 22:07:22.821887
1 2020-03-25 22:07:27.826093
2 2020-03-25 22:07:27.826093
actual output (see 4 instead of 2 prints at 17' and 3 instead of 2 prints at 27')
1 2020-03-25 22:07:17.817528
2 2020-03-25 22:07:17.817528
1 2020-03-25 22:07:17.817528
2 2020-03-25 22:07:17.817528
1 2020-03-25 22:07:22.821887
2 2020-03-25 22:07:22.821887
1 2020-03-25 22:07:27.826093
2 2020-03-25 22:07:27.826093
2 2020-03-25 22:07:27.826093
I dont actually know why sometimes thread triggers function more than just once. Any idea what i do wrong ?
Both threads add a task to schedule
and both threads execute run_pending()
. What might be happening here is that one thread executes run_pending()
causing both of the scheduled tasks to be run, but before run_pending()
finishes executing in the first thread (and marking the pending tasks as completed), the second thread steps in and executes run_pending()
as well, causing any pending tasks to be executed twice.
By moving the lock from the function p
and putting it around run_pending()
instead I was unable to replicate the behavior of tasks being fired twice.
def f(number):
schedule.every(5).seconds.do(p, number)
while True:
_lock.acquire()
schedule.run_pending()
_lock.release()
time.sleep(1)
Note that you could rewrite the program as well such that only one thread calls the run_pending()
, something like this:
import threading
import time
from datetime import datetime
import schedule
def p(number):
print(number, datetime.now())
schedule.every(5).seconds.do(p, 1)
schedule.every(5).seconds.do(p, 2)
def task_runner():
while True:
schedule.run_pending()
time.sleep(1)
thread = threading.Thread(target=task_runner)
thread.start()
This is assuming you want to use the main thread for something other than running the scheduled tasks. Or you could just put a call to run_pending()
in your application's event loop without having it in a separate thread.