harpan
harpan

Reputation: 8631

Run infinite loop in background and access class variable from the loop, with Python asyncio

I have a class Sensor which has one numeric value num. It has an async method update, which updates the num every second in an infinite loop.

I want to initiate the class and call the method update, keep it running and return the control to the main program to access the value of the num after some time. I am using asyncio but do not know how to invoke the method such that the concurrent running of the loop and variable access is feasible.

Code:

import asyncio
import time


class Sensor:
    def __init__(self, start=0):
        self.num = start

    async def update(self):
        print(f"Starting Updates{self.num}")
        while True:
            self.num += 1
            print(self.num)
            await asyncio.sleep(1)


if __name__ == "__main__":
    print("Main")
    sensor = Sensor()
    # asyncio.run(sensor.update())
    # asyncio.ensure_future(sensor.update())
    future = asyncio.run_coroutine_threadsafe(sensor.update(), asyncio.new_event_loop())
    print("We are back")
    print(f"current value: {sensor.num}")
    time.sleep(4)
    print(f"current value: {sensor.num}")

This gives me output of 0 before and after waiting for 4 seconds, which means the update method is not running behind. run() does not return the control at all.

Which method should I call to invoke the infinite loop in the background?

Upvotes: 0

Views: 1583

Answers (2)

Mohammad Esmaeilbeygi
Mohammad Esmaeilbeygi

Reputation: 84

According to documents for running tasks concurrently, you should use gather. For example, your code will be something like this:

import asyncio
import time


class Sensor:
    def __init__(self, start=0):
        self.num = start

    async def update(self):
        print(f"Starting Updates{self.num}")
        while True:
            self.num += 1
            print(self.num)
            await asyncio.sleep(1)


async def print_value(sensor):
    print("We are back")
    print(f"current value: {sensor.num}")
    await asyncio.sleep(4)
    print(f"current value: {sensor.num}")


async def main():
    print("Main")
    sensor = Sensor()
    await asyncio.gather(sensor.update(), print_value(sensor))

if __name__ == "__main__":
    asyncio.run(main())

Upvotes: 0

user4815162342
user4815162342

Reputation: 155056

To use asyncio.run_coroutine_threadsafe, you need to actually run the event loop, in a separate thread. For example:

sensor = Sensor()
loop = asyncio.new_event_loop()
threading.Thread(target=loop.run_forever).start()
future = asyncio.run_coroutine_threadsafe(sensor.update(), loop)
...
loop.call_soon_threadsafe(loop.stop)

Upvotes: 2

Related Questions