在 xlwings 中始终使用异步 UDF 更新单元是否有意义?

Does it make sense to always use asynchronous UDFs in xlwings for updating cells?

对于 return 将写入 Excel 单元格的结果的 xlwings UDF,始终使 UDF 异步以便 Excel 不会冻结是否有意义正在处理 UDF?

是否存在不使用异步 UDF 更好的情况?

https://docs.xlwings.org/en/stable/udfs.html#asynchronous-udfs

我正在使用 Excel 2016 和 Windows 10

我认为真正的问题是何时使用异步 UDF。无论我们是否处于 xlwings 的上下文中,这个问题的答案都是一样的。我会引用 MSFT:

Some user-defined functions must wait for external resources. While they wait, the Excel calculation thread is blocked. User-defined functions can run asynchronously. This frees the calculation thread to run other calculations while the user-defined function waits.

在 Excel 中大量使用异步函数会效率低下,但我也 怀疑它可能会导致错误和不正确的应用程序状态 。这是我的经验之谈,未经官方证实,期待专家意见。

如果您需要与 Excel 一起正常工作的异步函数,而不是稍后使用 COM 将值写回 Excel,您应该使用 Excel 的异步函数或RTD(实时数据)功能。 AFAIK,xlwings 不使用这些。

Excel 的异步函数不同于使用后台线程稍后将结果写回 Excel。它们在 Excel 计算周期内 运行,但允许同时进行 运行 计算,因此其他计算不会阻塞在一个等待 IO 上,例如。

如果你有一个很长的 运行ning 函数并且你不想等待它到 return,最好使用一个 RTD 函数,它在结果出现时更新一次准备好了。

PyXLL (https://www.pyxll.com) 支持异步和 RTD 函数。

https://www.pyxll.com/docs/userguide/udfs.html#asynchronous-functions https://www.pyxll.com/docs/userguide/rtd.html

另一种选择是使用线程安全函数。这些 运行 在 Excel 中的后台线程池中。如果您有可以 运行 并发的函数(即它们为 IO 或 CPU 密集型任务释放 GIL),那么这是提高工作表性能的简单方法。

要使用 PyXLL 将函数标记为线程安全的,只需在注册函数时指定 "thread_safe=True",例如:

from pyxll import xl_func

@xl_func(thread_safe=True)
def my_thread_safe_function():
    pass

你可以使用 xlOil(免责声明:我写的)来做到这一点。 xlOil 支持使用 Excel 的本机异步和 RTD 服务器的异步函数。 Python 插件的文档在这里:https://xloil.readthedocs.io/en/latest/xlOil_Python/index.html.

以下 python 模块声明了一个异步 Excel 函数 longRunningFunction:

import xloil
import asyncio

@xloil.func
async def longRunningFunction(returnVal, waitTime:int):
    await asyncio.sleep(waitTime)
    return returnVal

默认情况下,xlOil 使用 RTD 技术来完成这项工作,因为 Excel 的本机异步实际上与 UI 不异步。这意味着如果您以任何方式与 Excel 交互来停止计算,所有本机异步函数都会被取消。

RTD 与 UI 异步,但有一些开销,因此应仅在需要时使用。它还需要启用自动计算才能按预期工作,即返回的值出现在 Excel 中。 (尽管您可以使用 xloil.run_later() 在 UDF 末尾添加对 Application.Calculate 的延迟调用以避免此要求)。 xlOil Docs.

中讨论了相对优势

xlOil 使用 python 的 asyncio 库,因此需要一个可等待的函数。如果您要调用的 python 代码不是异步感知的,您始终可以 运行 在新进程中使用它并使用 concurrent.futures.ProcessPoolExecutor.

等待该进程