简体   繁体   中英

asyncio try to acquire a lock without waiting on it

I'm converting some threaded code to asyncio.

In the threaded code, I'm calling threading.RLock.acquire( blocking = False, timeout = 0 )

There doesn't seem to be a way to try to aquire an asyncio.Lock without also waiting on it. Is there a way to do this and if so, what am I missing?

In case it helps, here's my helper function:

@contextlib.contextmanager
def try_acquire_lock ( lock: gevent.lock.RLock ) -> Iterator[bool]:
    try:
        locked: bool = lock.acquire ( blocking = False, timeout = 0 )
        yield locked
    finally:
        if locked:
            lock.release()

and here's an example of how I use it:

    def generate( self, cti: Freeswitch_acd_api ) -> bool:
        log = logger.getChild( 'Cached_data._generate' )
        if self.data_expiration and tz.utcnow() < self.data_expiration:
            log.debug( f'{type(self).__name__} data not expired yet' )
        else:
            with try_acquire_lock( self.lock ) as locked:
                if locked:
                    log.debug( f'{type(self).__name__} regenerating' )
                    try:
                        new_data = self._generate( cti )
                    except Freeswitch_error as e:
                        log.exception( 'FS error trying to generate data: %r', e )
                        return False
                    else:
                        self.data = new_data
                        self.data_expiration = tz.utcnow() + tz.timedelta( seconds=self.max_cache_seconds )
        return True

Because somebody is bound to ask "why would you want to do this", it's because in some scenarios I have 3 different threads (now tasks) that each have a connection to a different server. These tasks are responsible for updating state using information from each of these servers. There is some information that is "global" that I can get from any one of the servers. If one task is already updating that global information, I don't want another task to repeat that effort, so I use a lock to control who's currently doing that process. The reason I need to be able to get the information from all the servers is because sometimes one will be taken down for maintenance and this was the simplest most fool-proof way I could think of to implement it without creating extra connections to the servers.

You can try to use the Lock.locked() method, which returns a boolean value indicating whether the lock is currently held or not. You can use this method to check if the lock is currently held by another task, and if it is, you can choose to skip the code that requires the lock or take some other action.

Example of how you can use Lock.locked() to try to acquire a lock without waiting:

import asyncio


async def main():
    lock = asyncio.Lock()

    if not lock.locked():
        # Lock is not held by another task, so we can acquire it
        async with lock:
            print("Acquired lock")
            # Do some work that requires the lock here
    else:
        # Lock is already held by another task, so we cannot acquire it
        print("Lock is held by another task, skipping code that requires the lock")

asyncio.run(main())

Or, you can use the Lock.acquire() method and pass it the blocking=False argument to try to acquire the lock without waiting. This will return True if the lock was acquired successfully and False if it was not.

    import asyncio
    
    async def main():
    lock = asyncio.Lock()
    
    if await lock.acquire(blocking=False):
        # Lock was acquired successfully
        print("Acquired lock")
        # Do some work that requires the lock here
        lock.release()
    else:
        # Lock is already held by another task, so we cannot acquire it
        print("Lock is held by another task, skipping code that requires the lock")

asyncio.run(main())

Tell me if it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM