I am developing a telegram bot using python-telegram-bot. It is like a stock screener, it analyzes the market every given interval and sends the results to users that are subscribed to the indicator. The problem is I do not want the bot to be blocked (users continuously interact with it) when it is analyzing the market (it fetches data & has a lot of computations). So I thought I needed to do that in a different thread, but I can’t make it work with the asyncio
Here is the example:
async def run_screener(bot):
while True:
async def heavy_computations():
for i in range(5):
await asyncio.sleep(2)
print("Doing computations")
compute = threading.Thread(target=lambda: asyncio.run(heavy_computations()))
compute.start()
compute.join() # <--- This is blocking the bot
# Computations are done, now send the results with the bot
async with bot: # <--- For some reason this line blocks the bot forever, even without .join()
for user_id in users:
await bot.send_message(text=results, chat_id=user_id)
await asyncio.sleep(compute_next_time())
async def main():
application = buildBot()
async with application:
await application.start()
await application.updater.start_polling()
await run_screener(application.bot)
await application.updater.stop()
await application.stop()
asyncio.run(main())
2
Answers
There is never any reason reason to
start
a thread and then immediatelyjoin
it. You are complaining thatcompute.join()
"blocks" the caller, but blocking the caller is the only thing thatjoin()
is ever supposed to do.compute.join()
does nothing to thecompute
thread. It does nothing at all to any thread or, to anything else.compute.join()
does nothing until thecompute
thread has finished, and then it returns. That’s it. That’s all it does.I don’t know anything about telegram. I don’t know what you actually are trying to do, but it sounds like you’ll need to re-structure your code so that
run_screener
does not wait until "Computations are done." Instead, you’ll either need to make the new thread itself "send the results with the bot," or else you’ll need to make the new thread trigger some event that will cause some other thread to "send the results with the bot."P.S., The reason why threads exist is, so you can do this:
And, the reason why
join
exists is, thatfoo
might produce a result that you want to use later:To run a non-asyncio task and await its completion without blocking other asyncio tasks tou use method
asyncio.loop.run_in_executor
. You can either execute your non-asyncio worker function in a multiprocessing pool or a multithreading pool. Since you are using multithreading we can either run the task in a default multithreading pool provided by the asyncio loop or create a custom pool, as we are doing here (in this case we only need a pool size of 1):Prints: