With a huge caveat, yes. You can use asyncio if the calls to send_message
can not block the process. This offers concurrency and not parallelism. It would look something like this:
import asyncio
async def main():
await start(None, None)
async def send_message(user, msg):
"""Has to be non-blocking.
That is, there has to be a way to instruct the loop to come back and
check later. For instance, if this is an api that returns a task
handle, you can call `asyncio.sleep(1)` between every call to check
the task status. That way the thread can move back to the loop for 1
second at a time.
"""
print(f"{msg} {user}")
async def start(update, context) -> None:
await asyncio.gather(
*[
asyncio.create_task(send_message(user, "hello"))
for user in [1111, 2222, 3333, 4444]
]
)
if __name__ == "__main__":
asyncio.run(main())
But that may not work in your case at all, if the call to send_message
waits for a response before continuing.
Here is a good answer to multiprocessing vs multithreading vs asyncio in Python 3