Non blocking python class method executed with asyncio - python

I'm trying to initialize a non-blocking task, which shares data with its parent object. It is a websocket client, and it would not block the main execution, though still run "forever".
My humble expectations were this would do it, but sadly, it is blocking the main thread.
loop = asyncio.new_event_loop()
task = loop.create_task(self.initWS())
loop.run_forever()

self.initWS() is indeed not blocking the main thread, but loop.run_forever() is.
If you want to execute more tasks concurrently with self.initWS(), you have to add them to the asyncio loop, too.

Related

Basic asyncio not executing second task

In this minimal example, I expect the program to print foo and fuu as the tasks are scheduled.
import asyncio
async def coroutine():
while True:
print("foo")
async def main():
asyncio.create_task(coroutine())
#asyncio.run_coroutine_threadsafe(coroutine(),asyncio.get_running_loop())
while True:
print("fuu")
asyncio.run(main())
The program will only write fuu.
My aim is to simply have two tasks executing concurrently, but it seems like the created task is never scheduled.
I also tried using run_coroutine_threadsafe with no success.
If I add await asyncio.sleep(1) to the main. The created task takes the execution and the program will only write foo.
What I am supposed to do to run two tasks simultaneously using asyncio ?
I love this question and the explanation of this question tells how asyncio and python works.
Spoilers - It works similar to Javascript single threaded runtime.
So, let's look at your code.
You only have one main thread running which would be continuously running since python scheduler don't have to switch between threads.
Now, the thing is, your main thread that creates a task actually creates a coroutine(green threads, managed by python scheduler and not OS scheduler) which needs main thread to get executed.
Now the main thread is never free, since you have put while True, it is never free to execute anything else and your task never gets executed because python scheduler never does the switching because it is busy executing while True code.
The moment you put sleep, it detects that the current task is sleeping and it does the context switching and your coroutine kicks in.
My suggestion. if your tasks are I/O heavy, use tasks/coroutines and if they are CPU heavy which is in your case (while True), create either Python Threads or Processes and then the OS scheduler will take care of running your tasks, they will get CPU slice to run while True.

Multiple asyncio loops

I am implementing a service in a multi-service python application. This service will wait for requests to be pushed in a redis queue and resolve them whenever one is available.
async def foo():
while True:
_, request = await self.request_que.brpop(self.name)
# processing request ...
And I'm adding this foo() function to an asyncio event loop:
loop = asyncio.new_event_loop()
asyncio.ensure_future(self.handle(), loop=loop)
loop.run_forever()
The main question is, is it ok to do this on several(say 3 or 4) different services, that results in running several asyncio loops simultaneously? Will it harm OS?
Simple answer: yes. It is fine. I previously implemented a service which runs on one asyncio loop and spawns additional processes which run on their own loop (within the same machine).

run infinite loop in asyncio event loop running off the main thread

I wrote code that seems to do what I want, but I'm not sure if it's a good idea since it mixes threads and event loops to run an infinite loop off the main thread. This is a minimal code snippet that captures the idea of what I'm doing:
import asyncio
import threading
msg = ""
async def infinite_loop():
global msg
while True:
msg += "x"
await asyncio.sleep(0.3)
def worker():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
asyncio.get_event_loop().run_until_complete(infinite_loop())
t = threading.Thread(target=worker, daemon=True)
t.start()
The main idea is that I have an infinite loop manipulating a global variable each 0.3 s. I want this infinite loop to run off the main thread so I can still access the shared variable in the main thread. This is especially useful in jupyter, because if I call run_until_complete in the main thread I can't interact with jupyter anymore. I want the main thread available to interactively access and modify msg. Using async might seem unnecessary in my example, but I'm using a library that has async code to run a server, so it's necessary. I'm new to async and threading in python, but I remember reading / hearing somewhere that using threading with asyncio is asking for trouble... is this a bad idea? Are there any potential concurrency issues with my approach?
I'm new to async and threading in python, but I remember reading / hearing somewhere that using threading with asyncio is asking for trouble...
Mixing asyncio and threading is discouraged for beginners because it leads to unnecessary complications and often stems from a lack of understanding of how to use asyncio correctly. Programmers new to asyncio often reach for threads by habit, using them for tasks for which coroutines would be more suitable.
But if you have a good reason to spawn a thread that runs the asyncio event loop, by all means do so - there is nothing that requires the asyncio event loop to be run in the main thread. Just be careful to interact with the event loop itself (call methods such as call_soon, create_task, stop, etc.) only from the thread that runs the event loop, i.e. from asyncio coroutines and callbacks. To safely interact with the event loop from the other threads, such as in your case the main thread, use loop.call_soon_threadsafe() or asyncio.run_coroutine_threadsafe().
Note that setting global variables and such doesn't count as "interacting" because asyncio doesn't observe those. Of course, it is up to you to take care of inter-thread synchronization issues, such as protecting access to complex mutable structures with locks.
is this a bad idea?
If unsure whether to mix threads and asyncio, you can ask yourself two questions:
Do I even need threads, given that asyncio provides coroutines that run in parallel and run_in_executor to await blocking code?
If I have threads providing parallelism, do I actually need asyncio?
Your question provides good answers to both - you need threads so that the main thread can interact with jupyter, and you need asyncio because you depend on a library that uses it.
Are there any potential concurrency issues with my approach?
The GIL ensures that setting a global variable in one thread and reading it in another is free of data races, so what you've shown should be fine.
If you add explicit synchronization, such as a multi-threaded queue or condition variable, you should keep in mind that the synchronization code must not block the event loop. In other words, you cannot just wait on, say, a threading.Event in an asyncio coroutine because that would block all coroutines. Instead, you can await an asyncio.Event, and set it using something like loop.call_soon_threadsafe(event.set) from the other thread.

Running functions in a thread class in the background with asyncio

This is my first attempt at using asyncio in a project. I'd like my class to initialize and run, with several of its functions running periodically "in the background". I'd like the class' init to return after starting these background tasks, so that it can continue to do its synchronous stuff at the same time.
What I have:
class MyClass(threading.Thread):
def __init__(self, param):
self.stoprequest = threading.Event()
threading.Thread.__init__(self)
self.param = param
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
asyncio.ensure_future(self.periodic(), loop=self.loop)
print("Initialized")
async def periodic(self):
while True:
print("I'm here")
await asyncio.sleep(1)
def run(self):
# continue to do synchronous things
I'm sure unsurprisingly, this doesn't work. I've also tried using a "normal" asyncio function with run_until_complete() in init, but of course init never returns then.
How can I run asyncio functions that belong to this class periodically in the background, while the rest of the class (run()) continues to do synchronous work?
Passing loop as argument to ensure_future doesn't start this loop. You should call run_until_complete or run_forever to force you coroutines being started, there's no other way to do it.
How can I run asyncio functions that belong to this class periodically
in the background, while the rest of the class (run()) continues to do
synchronous work?
You can't. Just as you can't run event loop and synchronious code simultaneously in the main thread. Loop starting - blocks thread's execution flow until loop is stopped. This is just how asyncio works.
If you want to run asyncio in background you should run it in separate thread and do your synchronous things in main thread. Example of how to do it can be found here.
It you need to run blocking code in thread alongside with asyncio most convenient way now is to run asyncio in the main thread and to run blocking code in a background thread using run_in_executor function. You can find example of doing it here.
It's important to say that asyncio itself usually is used in main thread (without other threads) to achieve benefits of asynchronous programming. Are you sure you need second thread? If not please read this answer to see why asyncio is used.

Tornado Ioloop in thread

I need to implement a simple service using Tornado. When my service starts it creates a second thread and i need to run Tornado's ioloop in this thread, because meanwhile the main thread is busy doing some other work. Is it ok to get ioloop instance using IOLoop.current() in second thread or there is another way to get ioloop instance for the thread? Docs say that current() returns main threads's ioloop instance if there's no running ioloop in current thread. I am a little confused at this point.
I see that the documentation is confusing. When your background thread begins, run this on the background thread:
io_loop = IOLoop()
io_loop.make_current()
After that, IOLoop.current(instance=False) on the background thread will return the thread's special IOLoop.

Categories