python - 在python中,调用后是否有可能发生异常,但其后的try block 为 "before"?

标签 python multithreading exception signals keyboardinterrupt

给定一个函数调用和紧随其后的 try block ,是否存在调用正常返回但引发异常且 try block 未捕获的情况?

例如:

# example 1
resource = acquire_a_resource()
try:
    resource.do_something()
    # some more code...
finally:
    resource.close()

是否有可能acquire_a_resource()正常返回,但resource.close()不会被调用?

或者换句话说,是否存在以下情况:

# example 2
resource = None
try:
    resource = acquire_a_resource()
    resource.do_something()
    # some more code...
finally:
    if resource:
        resource.close()

会比示例 #1 更安全吗?

也许是因为与 KeyboardInterrupt/threads/signals 有关?

最佳答案

是的,至少在理论上是这样,尽管在 CPython 中不是这样(详细信息请参阅脚注)。线程并不是特别相关,但您的 KeyboardInterrupt 场景恰到好处:

resource = acquire_a_resource()

调用该函数。该函数获取资源并返回句柄,然后在给变量赋值期间,1发生键盘中断。所以:

try:

不运行——而是发生KeyboardInterrupt异常,离开当前函数并解除变量绑定(bind)。

第二个版本通过了finally子句,因此假设if resource找到了boolean-truth-y,resource.close()确实接到电话了。

(请注意,实际触发此操作通常非常困难:您必须恰到好处地计时中断。您可以通过添加 time.sleep(1) 来大幅增加竞争窗口在尝试之前。)

在许多情况下,with 语句效果很好:

with acquire_a_resource() as resource:
    resource.do_something()

其中 close 内置于 __exit__ 方法中。即使该 block 通过异常转义,该方法也会运行。


1一般来说,实现有义务完成获取的资源到变量的绑定(bind),否则会出现不可恢复的竞争。在 CPython 中,会发生这种情况,因为解释器会检查语句之间的中断,有时还会检查源代码中的重要位置。

CPython 实际上添加了另一个特殊情况:

    /* Do periodic things.  Doing this every time through
       the loop would add too much overhead, so we do it
       only every Nth instruction.  We also do it if
       ``pendingcalls_to_do'' is set, i.e. when an asynchronous
       event needs attention (e.g. a signal handler or
       async I/O handler); see Py_AddPendingCall() and
       Py_MakePendingCalls() above. */

    if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.eval_breaker)) {
        opcode = _Py_OPCODE(*next_instr);
        if (opcode == SETUP_FINALLY ||
            opcode == SETUP_WITH ||
            opcode == BEFORE_ASYNC_WITH ||
            opcode == YIELD_FROM) {
            /* Few cases where we skip running signal handlers and other
               pending calls:
               - If we're about to enter the 'with:'. It will prevent
                 emitting a resource warning in the common idiom
                 'with open(path) as file:'.
               - If we're about to enter the 'async with:'.
               - If we're about to enter the 'try:' of a try/finally (not
                 *very* useful, but might help in some cases and it's
                 traditional)
               - If we're resuming a chain of nested 'yield from' or
                 'await' calls, then each frame is parked with YIELD_FROM
                 as its next opcode. If the user hit control-C we want to
                 wait until we've reached the innermost frame before
                 running the signal handler and raising KeyboardInterrupt
                 (see bpo-30039).
            */
            goto fast_next_opcode;
        }

(Python/ceval.c,第 1000 行附近)。

实际上,try确实运行了,因为这里有一个SETUP_FINALLY。我根本不清楚其他 Python 实现是否也做同样的事情。

关于python - 在python中,调用后是否有可能发生异常,但其后的try block 为 "before"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56031248/

相关文章:

python - 如何通过字符串包含合并基于两列的 2 个数据框

java - 为什么线程池只创建一个线程?

java - 为什么我不能在 try catch 语句中调用的方法中抛出异常(已检查)?

python - 值错误: signal only works in main thread when using Stomp

python - 如何将基于 pandas 数据框的图形导出为 pdf?

c++ - Linux 上 C++ 中线程/共享内存之间的线程安全数据交换

vb.net - VB.NET 中从另一个线程挂起一个线程

android - "Can' t compress a recycled bitmap”单个设备异常

java - BindException 多线程服务器

python - Sublime Text 3插件Python 3从其他文件夹导入包