我正在学习使用
multiprocessing
模块。现在我关注下面这句话:
请注意,安全分叉多线程进程是有问题的。
出现在官方文档中专门用于上下文和启动方法的部分。
关于这个主题,我已阅读这个答案,其中包含以下声明:
“请注意,安全分叉多线程进程是有问题的”:这里 problematic 是“不可能”的委婉说法。
我已经编写了这个测试代码(针对Linux平台):
import multiprocessing
import threading
import time
a = 1
t1 = None
def thread_function_1():
global a
while True:
time.sleep(2)
a += 1
def p1():
global a
global t1
while True:
time.sleep(1)
print(f"Process p1 ---> a = {a}, t1 = {t1}, t1 is_alive = {t1.is_alive()}")
if __name__ == "__main__":
multiprocessing.set_start_method('fork')
t1 = threading.Thread(target = thread_function_1)
t1.start()
p = multiprocessing.Process(target=p1)
p.start()
while True:
a += 1
time.sleep(1)
print(f"Thread Main ---> a = {a}, t1 = {t1}, t1 is_alive = {t1.is_alive()}")
我在 Linux 上运行代码,因此说明:
multiprocessing.set_start_method('fork')
选择一种启动方法,该方法已经是 Linux 上的默认启动方法。这是因为对于测试,我使用的是 Python 3.6(来自文档:默认启动方法将不再是 Python 3.14 中的 fork。)。
其执行的输出为:
Thread Main ---> a = 2, t1 = <Thread(Thread-1, started 140618847368960)>, t1 is_alive = True
Process p1 ---> a = 1, t1 = <Thread(Thread-1, stopped 140618847368960)>, t1 is_alive = False
Thread Main ---> a = 4, t1 = <Thread(Thread-1, started 140618847368960)>, t1 is_alive = True
Process p1 ---> a = 1, t1 = <Thread(Thread-1, stopped 140618847368960)>, t1 is_alive = False
Thread Main ---> a = 5, t1 = <Thread(Thread-1, started 140618847368960)>, t1 is_alive = True
Process p1 ---> a = 1, t1 = <Thread(Thread-1, stopped 140618847368960)>, t1 is_alive = False
Thread Main ---> a = 7, t1 = <Thread(Thread-1, started 140618847368960)>, t1 is_alive = True
Process p1 ---> a = 1, t1 = <Thread(Thread-1, stopped 140618847368960)>, t1 is_alive = False
输出显示进程
p1
具有对全局变量t1
的引用,该变量指向类Thread
的实例。但是在主进程中,线程处于状态started
,当在进程中时p1
处于状态stopped
。另请参阅方法 is_alive()
返回的不同值(Main 中的 true,p1
中的 false)。
p1
的值始终为a
,而在主进程中,变量1
的值由线程增加t1
。
为什么子进程继承的线程处于停止状态,而父进程继承的线程处于启动状态?
此行为与句子 “请注意,安全分叉多线程进程是有问题的。”?
为什么子进程继承的线程处于停止状态,而父进程继承的线程处于启动状态?
我想最好有一些状态表明该线程根本不存在。但是该线程在子进程中停止,因为该线程在子进程的上下文中从未存在过。
如果您问为什么线程不在子进程中运行,那是因为这会导致绝对的混乱。假设该线程位于与某些服务交互的库函数中。复制线程可能会导致该服务上的操作序列无效。
此行为与“请注意,安全分叉多线程进程是有问题的。”这句话相关联?
我想是这样。例如,假设当您调用
fork
时,其他线程之一持有锁。该锁永远不会在子进程中释放,因为解锁它的线程不存在。