Reputation: 1065
Consider this simplified example:
def get_external_thing() -> str:
my_variable = a_blocking_operation()
my_variable.some_other_operation()
return my_variable
externally_gathered_thing = None
while True:
sleep(1)
externally_gathered_thing = get_external_thing()
if externally_gathered_thing:
do_something_1()
externally_gathered_thing = None
do_something_2()
This will obviously enter the loop, sleep for a second, then go into get_external_thing()
and wait until a_blocking_operation()
is finished. Nothing else will be executed as long as get_external_thing()
is running.
What I'm trying to accomplish is force my loop to continue if get_external_thing()
is not finished and go straight to do_something_2()
. However, if get_external_thing()
is finished and externally_gathered_thing
has some value, I want do_something_1()
to be executed as well.
How can I accomplish it purely in Python? I tried to use this example to learn asyncio but didn't manage to produce any working example. Due to project requirements asyncio is a preferred but it's not a must.
In other words, I'd like do_something_2()
to be executed every second (or every second + some small overhead), regardless of the results of get_external_thing()
.
Note: don't be scared by the while True
construct, it's designed to run continuously on a raspberry pi :)
Upvotes: 3
Views: 79
Reputation: 155505
For this kind of task look into the concurrent.futures
module. For example:
def get_external_thing() -> str:
my_variable = a_blocking_operation()
my_variable.some_other_operation()
return my_variable
externally_gathered_thing = None
executor = concurrent.futures.ThreadPoolExecutor()
working = None
while True:
if working is None:
# if no work is in progress, start the external task in a bg thread
working = executor.submit(get_external_thing)
try:
# wait for the external result, but no more than a second
externally_gathered_thing = working.result(timeout=1)
working = None
except concurrent.futures.TimeoutError:
# in case of timeout, proceed with our logic anyway, we'll get
# back to waiting in the next iteration
pass
if externally_gathered_thing is not None:
do_something_1()
externally_gathered_thing = None
do_something_2()
An asyncio-based solution is possible, but it would still have to use threads under the hood to await a blocking operation (that's how run_in_executor
works), so it would combine the complexity of asyncio with the complexity of threads.
Upvotes: 1