简体   繁体   English

asyncio 尝试获取锁而不等待它

[英]asyncio try to acquire a lock without waiting on it

I'm converting some threaded code to asyncio.我正在将一些线程代码转换为 asyncio。

In the threaded code, I'm calling threading.RLock.acquire( blocking = False, timeout = 0 )在线程代码中,我调用 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.似乎没有办法在不等待的情况下尝试获取 asyncio.Lock。 Is there a way to do this and if so, what am I missing?有没有办法做到这一点,如果是这样,我错过了什么?

In case it helps, here's my helper function:如果有帮助,这是我的助手 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.因为肯定有人会问“你为什么要这样做”,这是因为在某些情况下我有 3 个不同的线程(现在是任务),每个线程都连接到不同的服务器。 These tasks are responsible for updating state using information from each of these servers.这些任务负责使用来自每个服务器的信息更新 state。 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.可以尝试使用 Lock.locked() 方法,返回一个 boolean 值表示当前是否持有锁。 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:如何使用 Lock.locked() 尝试在不等待的情况下获取锁的示例:

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.或者,您可以使用 Lock.acquire() 方法并将 blocking=False 参数传递给它,以尝试在不等待的情况下获取锁。 This will return True if the lock was acquired successfully and False if it was not.如果成功获取锁,则返回 True,否则返回 False。

    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.告诉我它是否有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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