Skip to content

Instantly share code, notes, and snippets.

@dmfigol
Last active April 21, 2024 17:32
Show Gist options
  • Save dmfigol/3e7d5b84a16d076df02baa9f53271058 to your computer and use it in GitHub Desktop.
Save dmfigol/3e7d5b84a16d076df02baa9f53271058 to your computer and use it in GitHub Desktop.
Python asyncio event loop in a separate thread
"""
This gist shows how to run asyncio loop in a separate thread.
It could be useful if you want to mix sync and async code together.
Python 3.7+
"""
import asyncio
from datetime import datetime
from threading import Thread
from typing import Tuple, List, Iterable
import httpx
URLS = [
"https://pypi.org",
"https://python.org",
"https://google.com",
"https://amazon.com",
"https://reddit.com",
"https://stackoverflow.com",
"https://ubuntu.com",
"https://github.com",
"https://microsoft.com",
]
def start_background_loop(loop: asyncio.AbstractEventLoop) -> None:
asyncio.set_event_loop(loop)
loop.run_forever()
async def fetch(url: str) -> Tuple[str, int]:
"""Does HTTP get on url and returns url and status code"""
async with httpx.AsyncClient() as session:
response = await session.get(url)
return url, response.status_code
async def fetch_all_urls(urls: Iterable[str]) -> List[Tuple[str, int]]:
"""Fetch all urls from the list of urls
It is done concurrently and combined into a single coroutine"""
tasks = [asyncio.create_task(fetch(url)) for url in urls]
results = await asyncio.gather(*tasks)
return results
def main() -> None:
loop = asyncio.new_event_loop()
t = Thread(target=start_background_loop, args=(loop,), daemon=True)
t.start()
start_time = datetime.now()
task = asyncio.run_coroutine_threadsafe(fetch_all_urls(URLS), loop)
for url, status_code in task.result():
print(f"{url} -> {status_code}")
exec_time = (datetime.now() - start_time).total_seconds()
print(f"It took {exec_time:,.2f} seconds to run")
loop.stop()
if __name__ == "__main__":
main()
@yaniaular
Copy link

Thank you so much!!!! It was very helpfull!!

@ColorBuffer
Copy link

You saved my day :))

@13hakta
Copy link

13hakta commented Oct 11, 2020

Thank you! One headache less

@bhopkinson
Copy link

Worked great - thank you.

@Marenostrum81
Copy link

Thanks a million

@rwang0417
Copy link

Thanks so much!

@igorbezr
Copy link

Thank you very much for your solution !
It helps me a lot : )

@kernelplv
Copy link

"mix sync and async code together" ❤

@ChangizShahdi
Copy link

Thanks a lot😍😍
It's realy helped me🙏🏻

@mbwmbw1337
Copy link

This is excellent. Thank you!

@tulinev
Copy link

tulinev commented Oct 22, 2021

👍

@homelyseven250
Copy link

Thanks so much, I can't tell you how much this has helped me.

@rob4226
Copy link

rob4226 commented Nov 21, 2021

Works great!! 🚀 Thank you!

@ks-mw
Copy link

ks-mw commented Jan 21, 2022

Thank you!

@gholamim6
Copy link

Nice.
all asyncio tutorials talk about run threads in a syncio program.
but your example is so much nice.
I always want to create a thread for asyncio and send all asynchronous task in to it and works with synchronous in the main thread.
but I didn't know how to do it exactly.
because I want to build a gui app that need some asynchronous results.
and all gui framework have their own event loop.
Thanks a lot.

@mrthere3
Copy link

thank you !!

@Y4r0z
Copy link

Y4r0z commented Jul 27, 2022

Thank you!

@httran13
Copy link

ty, you are a beast!

@jryebread
Copy link

Thank you, simple and straight to the point, the docs online for async stuff are trash

@homelyseven250
Copy link

homelyseven250 commented Jan 31, 2023 via email

@eggegg2001
Copy link

Nice. all asyncio tutorials talk about run threads in a syncio program. but your example is so much nice. I always want to create a thread for asyncio and send all asynchronous task in to it and works with synchronous in the main thread. but I didn't know how to do it exactly. because I want to build a gui app that need some asynchronous results. and all gui framework have their own event loop. Thanks a lot.

so do i.

@HorridModz
Copy link

Thanks so much! I was trying to get this to work for 3 days, and when I finally found this, it worked first try!

@HorridModz
Copy link

I wrote some helper functions for this:

from typing import Coroutine
import asyncio
from asyncio import Future, AbstractEventLoop
from threading import Thread


def create_event_loop_thread() -> AbstractEventLoop:
    """
    From https://gist.github.com/dmfigol/3e7d5b84a16d076df02baa9f53271058
    """
    def start_background_loop(loop: AbstractEventLoop) -> None:
        asyncio.set_event_loop(loop)
        loop.run_forever()

    eventloop = asyncio.new_event_loop()
    thread = Thread(target=start_background_loop, args=(eventloop,), daemon=True)
    thread.start()
    return eventloop

def run_coroutine_in_thread(coro: Coroutine) -> Future:
    """
    From https://gist.github.com/dmfigol/3e7d5b84a16d076df02baa9f53271058
    """
    return asyncio.run_coroutine_threadsafe(coro, self.eventloop)

@fhrzn
Copy link

fhrzn commented Feb 9, 2024

Thank you so much! It really works on the first try. I have been looking for this solution all this time, now my bug is solved. Thank you!!

@enjoysmath
Copy link

Saved my life 😯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment