我有一个黑匣子函数列表(没有关键数据处理,没有调用的副作用,可以提前终止),并且需要汇总它们的返回值:
aggregate = [result for fn in seq for result in fn()]
这些函数通常在几微秒内返回,但是有一些我无法影响的极端情况导致它们中的某些情况偶尔引发异常,返回无效类型(例如,None
而不是可迭代的)或无限期挂起。
为了处理无效的输出和异常,我编写了一个装饰器:
def safe_call(fn, valid_type, default, error_handler=None):
try:
val = fn()
if isinstance(val, valid_type):
return val
if error_handler is not None:
error_handler(TypeError("Invalid return type"))
except Exception as e:
if error_handler is not None:
error_handler(e)
return default
现在唯一的问题是挂起。上面的整个表达式应始终在毫秒内求值,并确保我试图为序列中的每个函数实现1ms超时。
我尝试了线程和多处理,但是我无法正确终止有问题的线程,并且启动新进程的开销过高,其本身超过了1毫秒的超时值。
我尝试使用信号,基准表明每次调用的开销为25%,这是可以接受的,但是评估是从django的观点进行的,它不在主线程中运行,因此信号无法正常工作。
我当前的解决方法是为每个函数创建一个单独的进程,并通过一个队列不断与其父进程进行同步,以等待被调用。这样,父级可以按需触发呼叫,等待1毫秒,然后检查结果是否已发回-如果有结果则保存结果,否则终止过程。
就性能而言,它确实可以工作,但是此解决方案有1400-1700%的内存开销,这是不可接受的。
那我如何以<1 ms的精度使函数调用超时而又没有巨大的内存开销?