最高返回执行 - 超时

Posted

技术标签:

【中文标题】最高返回执行 - 超时【英文标题】:Highest Returned Exec - With Timeout 【发布时间】:2022-01-13 22:52:28 【问题描述】:

我有一个 exec 输入列表,想知道哪个输入会将全局变量设置为最大值。目前,我的代码如下:

s1 = """
global a
a = 1"""

s2 = """
global a
a = 2"""

inputs = [s1, s2]

maxA = 0
for s in inputs:
    exec(s)
    maxA = max([maxA, a])
print(maxA)

打印正确的结果。

问题是我想限制每次调用的运行时间(比如 10 秒,对于这个例子)。我发现执行此操作的方法利用了多处理,例如:

import multiprocessing

s1 = """
global a
a = 1"""

s2 = """
global a
a = 2"""

inputs = [s1, s2]

maxA = 0
a = 0

def Execute_For_Multiprocessing(s):
    exec(s)
    global maxA
    maxA = max([maxA, a])
    print(maxA)
    return

for s in inputs:
    p = multiprocessing.Process(target=Execute_For_Multiprocessing, args = [s])
    p.start()
    p.join(10)

    if p.is_alive():
        p.terminate()
        p.join()

print(maxA)

但是,这不会打印正确的输出。似乎在多处理中,没有任何方法可以修改全局变量,因此即使在 Execute_For_Multiprocessing 中正确计算了值,它们也没有存储在它之外。

有人对此有解决方法吗?似乎以下任何一项都可以解决问题:

    一种在多处理调用中修改全局变量的方法 不使用多处理的函数调用超时方法 输入字符串的替代结构,允许我们从中提取有意义的返回值。

【问题讨论】:

我回答了您的问题以获得正确的结果,但我不知道超时函数调用与您修改全局变量以通过子进程获得正确结果的问题有什么关系。我真的不知道您的最终目标是什么,但无论如何,我希望您发布的代码不是您建议的实现方式。 【参考方案1】:

问题在于每个进程都在自己的地址空间中运行,因此拥有自己正在更新的全局变量副本。实际上,如果您在使用 fork 来创建新进程的平台上运行(顺便说一下,您应该使用您运行的平台标记您的问题 - - 这很重要),每个新创建的进程都从创建新进程时主进程拥有的地址空间的副本开始,但是当它修改该地址空间中的任何内容时,就会制作一个新副本(复制-写语义)。因此,新创建的进程对全局变量所做的任何更改都不会反映回主进程。然而:

如果您使用multiprocessing.Value 在共享内存中创建变量,例如a = multiprocessing.Value('i', 0, lock=False),则a 是对指向某个位置的值的引用,该位置是共享内存,任何地址中的任何进程都可以访问空间,即使此引用被复制,它仍然有效:

import multiprocessing

a = multiprocessing.Value('i', 0, lock=False)
maxA = multiprocessing.Value('i', 0, lock=False)

s1 = """
global a
a.value = 1"""

s2 = """
global a
a.value = 2"""

inputs = [s1, s2]

maxA.value = 0
a.value = 0

def Execute_For_Multiprocessing(s):
    exec(s)
    global maxA
    maxA.value = max(maxA.value, a.value)
    print(maxA.value)
    return

for s in inputs:
    p = multiprocessing.Process(target=Execute_For_Multiprocessing, args = [s])
    p.start()
    p.join()

print(maxA.value)

打印:

1
2
2

为 Windows 修改的相同程序:

import multiprocessing

a = multiprocessing.Value('i', 0, lock=False)
maxA = multiprocessing.Value('i', 0, lock=False)

def Execute_For_Multiprocessing(s):
    exec(s)
    global maxA
    maxA.value = max(maxA.value, a.value)
    print(maxA.value)
    return

# required for Windows:

if __name__ == '__main__':

    s1 = """
global a
a.value = 1"""

    s2 = """
global a
a.value = 2"""

    inputs = [s1, s2]

    maxA.value = 0
    a.value = 0

    for s in inputs:
        p = multiprocessing.Process(target=Execute_For_Multiprocessing, args = [s])
        p.start()
        p.join()

    print(maxA.value)

打印:

1
2
0

这对 Windows 不起作用的原因是 Windows 使用 spawn 方法启动新进程。这意味着新进程的初始化相当于创建一个新的空地址空间,然后启动一个新的 Python 解释器,该解释器通过重新读取源程序并在全局范围内执行所有语句来初始化该地址,然后才这样做调用您指定的目标函数。但这样做时,每个新创建的进程都在重新执行创建 multiprocessing.Value 实例的语句,因此将不再引用主进程创建的相同实例。

解决方案是将主进程创建的共享内存值传递给子进程,并让子进程使用这些值初始化全局内存:

import multiprocessing


def Execute_For_Multiprocessing(s, v1, v2):
    global maxA, a

    maxA = v1
    a = v2

    exec(s)
    maxA.value = max(maxA.value, a.value)
    print(maxA.value)
    return

# required for Windows:

if __name__ == '__main__':

    s1 = """
global a
a.value = 1"""

    s2 = """
global a
a.value = 2"""

    inputs = [s1, s2]

    a = multiprocessing.Value('i', 0, lock=False)
    maxA = multiprocessing.Value('i', 0, lock=False)
    # These statements are actually unnecessary:
    #maxA.value = 0
    #a.value = 0

    for s in inputs:
        p = multiprocessing.Process(target=Execute_For_Multiprocessing, args = [s, maxA, a])
        p.start()
        p.join()

    print(maxA.value)

打印:

1
2
2

当然,此代码也适用于使用 fork 的平台,例如 Linux。

【讨论】:

这回答了你的问题吗?

以上是关于最高返回执行 - 超时的主要内容,如果未能解决你的问题,请参考以下文章

paramiko执行命令超时的问题

Qt:如何执行超时函数

nginx后端服务器返回给nginx502504404执行超时等错误状态的解决方法

feign服务间调用超时时间

runloop

限定页面执行时间,请求超时抛出异常或提示