0

My code execution does not reach the print statement: print("I want to display after MyClass has started")

Why is this? I thought the purpose of await asyncio.sleep() is to unblock execution of code so that subsequent lines of code can run. Is that not the case?

import asyncio

class MyClass:
    def __init__(self):
        self.input = False
        asyncio.run(self.start())
        
        print("I want to display after MyClass has started")  #This line is never reached.
        
        
    async def start(self):
        while True:
            print("Changing state...")
            if self.input:
                print("I am on.")
                break
            await asyncio.sleep(1)

m = MyClass()
m.input = True  #This line is never reached!  Why?
print("I want to display after MyClass is started")

When I execute, it keeps printing "Changing state...". Even when I ctrl+c to quit, the execution continues as shown below. How can I properly terminate the execution? Sorry, I am new to python.

enter image description here

EDIT: I appreciate the common use of asyncio is for running two or more separate functions asynchronously. However, my class is one which will be responding to changes in its state. For example, I intend to write code in the setters to do stuff when the class objects attributes change -WHILE still having a while True event loop running in the background. Is there not any way to permit this? I have tried running the event loop in it's own thread. However, that thread then dominates and the class objects response times run into several seconds. This may be due to the GIL (Global Interpreter Lock) which we can do nothing about. I have also tried using multiprocessing, but then I lose access to the properties and methods of the object as parallel process run in their own memory spaces.

IqbalHamid
  • 2,324
  • 1
  • 18
  • 24

1 Answers1

2

In the init method of MyClass you invoke asyncio.run() - this method will execute the required routine until that routine terminates. In your case, since the main method includes a while True loop, it will never terminate.

Here is a slight modification of your code that perhaps shows the concurrency effect you're after -

import asyncio
class MyClass:
def __init__(self):
    self.input = False
    asyncio.run(self.main())

    print("I want to display after MyClass has been initialized.")  # This line is never reached.

async def main(self):
    work1 = self.work1()
    work2 = self.work2()
    await asyncio.gather(work1, work2)

async def work1(self):
    for idx in range(5):
        print('doing some work 1...')
        await asyncio.sleep(1)

async def work2(self):
    for idx in range(5):
        print('doing some work 2...')
        await asyncio.sleep(1)
m = MyClass()
print("I want to display after MyClass is terminated")
Alon Gadot
  • 555
  • 3
  • 10
  • Thank you Alon, shortly after asking my question, I figured out a similar solution to yours which seems to work; though I do not understand what is going on. I created two functions like your work1() and work2(). Except I called them using await asyncio.wait([ asyncio.create_task(work1()), asyncio.create_task(work2()), ]) What is the difference between yours and mine? What is the difference between calling await asyncio.gather(work1, work2) and await asyncio.wait(.....)? – IqbalHamid Jul 10 '21 at 16:12
  • Hey Iqbal, asyncio.wait and asyncio.gather are similar but somewhat different- you should look into the answers here https://stackoverflow.com/questions/42231161/asyncio-gather-vs-asyncio-wait for specifics. Overall, asyncio.wait is more low level and allows fine grained control. – Alon Gadot Jul 11 '21 at 17:18