Python3 中处理交错锁

问题描述 投票:0回答:1

我正在尝试在Python3中实现以下逻辑:

def f():

  lock1.acquire()

  task_protected_by_lock1() # Might acquire lock2 internally

  lock2.acquire()

  task_protected_by_lock1_and_lock2()

  lock1.release()

  task_protected_by_lock2() # Might acquire lock1 internally

  lock2.release()

但是,我发现无法正确处理 SIGINT,因为它会在随机位置引发 KeyBoardInterrupt 异常。我需要保证当控制流退出时,lock1和lock2都被释放

f()
(即正常返回或未处理的异常)。

我知道 SIGINT 可以暂时被屏蔽。然而,正确恢复面具成为另一个挑战,因为它可能已经被外部掩盖了。此外,锁之间执行的任务也可能会调整信号掩码。我相信必须有更好的解决方案。

我想知道是否有一种方法可以让我利用上下文管理器(

with
声明)来实现它。我考虑过以下内容,但没有一个适合我的用例:

方法 1 - 单一
with
声明

def f():
  with lock1, lock2:
    task_protected_by_lock1() # Bad: acquiring lock2 internally will cause deadlock
    task_protected_by_lock1_and_lock2() # Good
    task_protected_by_lock2() # Bad: acquiring lock1 internally will cause deadlock

方法 2 - 嵌套
with
语句

def f():
  with lock1:
    task_protected_by_lock1() # Good
    with lock2:
      task_protected_by_lock1_and_lock2() # Good
      task_protected_by_lock2() # Bad: acquiring lock1 internally will cause deadlock

方法 3 - 手动锁管理

def f():

  flag1 = False
  flag2 = False

  try:
    lock1.acquire()
    # Bad: SIGINT might be raised here
    flag1 = True

    task_protected_by_lock1()

    lock2.acquire()
    # Bad: SIGINT might be raised here
    flag2 = True

    task_protected_by_lock1_and_lock2()

    lock1.release()
    # Bad: SIGINT might be raised here
    flag1 = False

    task_protected_by_lock2()

    lock2.release()
    # Bad: SIGINT might be raised here
    flag2 = False

  except Exception as e:

    if flag1:
      lock1.release()

    if flag2:
      lock2.release()

    raise e

方法 4 - 与 3 类似,但更棘手

def f():
  try:
    lock1.acquire()

    task_protected_by_lock1()

    lock2.acquire()

    task_protected_by_lock1_and_lock2()

    lock1.release()

    # Suppose SIGINT happened here, just after another thread acquired lock1

    task_protected_by_lock2()

    lock2.release()

  except Exception as e:

    if lock1.locked():
      lock1.release() # Bad: lock1 is NOT acquired by this thread!

    if lock2.locked():
      lock2.release()

    raise e

方法 5 - 破坏一致性且效率低下

def f():
  with lock1:
    task_protected_by_lock1()

  # Bad: other thread might acquire lock1 and modify protected resources.
  # This is NOT supposed to happen.

  with lock1, lock2:
    task_protected_by_lock1_and_lock2()

  # Bad: other thread might acquire lock2 and modify protected resources.
  # This is NOT supposed to happen.

  with lock2:
    task_protected_by_lock2()
python python-3.x python-multithreading contextmanager
1个回答
0
投票

我认为这就是 asyncio 的闪光点。

import asyncio

async def task_protected_by_lock1():
    print("task_protected_by_lock1")

async def task_protected_by_lock1_and_lock2():
    print("task_protected_by_lock1_and_lock2")

async def task_protected_by_lock2():
    print("task_protected_by_lock2")

async def f():
    try:
        await task_protected_by_lock1()
        await task_protected_by_lock1_and_lock2()
        await task_protected_by_lock2()
    except KeyboardInterrupt:
        print("sigint")
    except Exception as e:
        print("handle the exception you like")

if __name__ == "__main__":
    asyncio.run(f())
© www.soinside.com 2019 - 2024. All rights reserved.