我有一个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中正确计算了值,也没有一个值存储在它之外。
有人能解决这个问题吗?以下任何一项似乎都能解决问题:
- 从多处理调用中修改全局变量的方法
- 不使用多处理的函数调用的超时方法
- 输入字符串的另一种结构允许我们从中提取有意义的返回值
问题是,每个进程都在自己的地址空间中运行,因此有自己的全局变量副本,它正在更新。事实上,如果你在一个使用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。