DEV Community

Hai Nunez
Hai Nunez

Posted on

computer

Margaret found Timothy in the library's coordination room, staring at a wall covered in task dependency charts. 'The Event pattern works beautifully for simple signals,' he said, 'but I've hit something more complex. I need tasks to wait for specific conditions to become true, then wake up and check again. Events are too blunt - they're either set or not set. I need something more nuanced.'

'Ah,' Margaret smiled, 'you need a Condition. Think of it as a smart waiting room where tasks can sleep until they're told that something interesting has changed, then wake up to check if the change matters to them specifically.'

The Problem: When Events Aren't Enough

Timothy showed Margaret his code for managing a shared resource with capacity limits:

import asyncio

# Attempt 1: Using Event (doesn't quite work)
class SharedResource:
    def __init__(self, capacity):
        self.capacity = capacity
        self.in_use = 0
        self.available_event = asyncio.Event()
        self.available_event.set()  # Initially available

    async def acquire(self):
        while True:
            await self.available_event.wait()

            if self.in_use < self.capacity:
                self.in_use += 1
                if self.in_use >= self.capacity:
                    self.available_event.clear()
                return

            # Race condition: another task might have grabbed it                                                                                                                                                          ___eWEyOS5hMEFUaTZLMnRUUmlEUUZBVENfN3J0U3VsR19pT0plU2tnY2xZRDRTdXJMeFZ3eENsNC1EMnppZEdwNGNWamhGVXE0dmcxSncyNnJJeTZVc0lNWGVTQVg2SGxSR1BySjh1Y3pzLW5EVXlhckNkYUpqb0Fqd284cWY2NVdGSnJzbHhlVXpsV0tLRnFCQk5KNlo1RUt0RjdqRFpnN2MzaDA3dTVCX1ZRSXVCbnBSVWd5WF9zekp2dTYxdG1ySjlkckRpeGREdFRxc1lhQ2dZS0FZWVNBUkVTRlFIR1gyTWlFUVJIZ1FOQ2xNNjNpWXFPUlVvRVNnMDIwNg==___
            # We need to wait again, but the event is still set!

    async def release(self):
        self.in_use -= 1
        self.available_event.set()

async def demonstrate_event_problem():
    resource = SharedResource(capacity=2)

    async def worker(worker_id):
        print(f'Worker {worker_id} trying to acquire')
        await resource.acquire()
        print(f'Worker {worker_id} acquired resource')
        await asyncio.sleep(2)
        await resource.release()
        print(f'Worker {worker_id} released resource')

    # Create 5 workers competing for 2 slots
    await asyncio.gather(*[worker(i) for i in range(5)])

# asyncio.run(demonstrate_event_problem())

Enter fullscreen mode Exit fullscreen mode

Top comments (0)