DmUser
DmUser

Reputation: 69

asyncio, this event loop is already running issue

is it a good idea to run the asyncio eventloop inside a thread?

import asyncio
import time
from sample_threading import parallel
loop = asyncio.new_event_loop()

async def fn(p):
  for i in range(5):
    print(i)
    time.sleep(5)
  print("done")


@parallel
def th(p):
   loop.run_until_complete(fn(p))

th(1)
th(2)
th(3)

above code giving error

raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running

any suggestion ?

Upvotes: 4

Views: 5284

Answers (2)

Sauron
Sauron

Reputation: 1353

error message you are haveing, This event loop is already running, is beacuse when you attempt to run an asyncio event loop that is already running. In your code, you are creating a new event loop using asyncio.new_event_loop(), but you are not explicitly setting it as the current event loop.

import asyncio
import time
from sample_threading import parallel

async def fn(p):
    for i in range(5):
        print(i)
        # instead of time.sleep, you ensure that the sleep operation is non- 
        # blocking and allows other tasks to run concurrently.
        await asyncio.sleep(5)
    print("done")

@parallel
def th(p):
    asyncio.run(fn(p))

th(1)
th(2)
th(3)

Running the asyncio event loop inside a thread can be a valid approach in certain scenarios, especially when you need to perform asynchronous tasks concurrently with other operations.

Upvotes: 1

jsbueno
jsbueno

Reputation: 110311

You can do that, but you have to explicitly start the event loop inside one - and only one - thread. I do not know the sample_threading.parallel decorator by heart, but I suppose it is automatically creating one thread for each call of your function?

In this case, when the second call is executed, the function runs in the second worker-thread, and tries to reuse the same loop you got a reference to in the global scope. It will work for the first worker thread, since the loop is not yet running, but will fail when you try it again.

Simply create one event loop for each thread you will have. I am not sure if having the threads created in an implicit and invisible way with this helper lib will make for a more maintainable project. (It already made the understanding of this initial problem harder, for example) - using the traditional threading.Thread() and thread.start() calls, with hard references to the running worker threads will likely be a better choice.

Other than that, just call asyncop.new_event_loop() in a function that runs inside your target worker threads, not on the global scope, and you should be good:


import asyncio
import time
import threading

async def fn(p):
  for i in range(5):
    print(i)
    time.sleep(5)
  print("done")

def thread_driver(p):
    loop = asyncio.new_event_loop()
    loop.run_until_complete(fn(p))



threads = [threading.Thread(target=thread_driver, args=(i,)) for i in range(1,4)]
[t.start() for t in threads()]


Upvotes: 2

Related Questions