如何确保一个类是跨进程安全的?

2022-04-19 12:28:51 +08:00
 LeeReamond

需求,一个异步脚本里大量使用run_in_executor()封装同步调用,然后脚本本身用了 os.fork()搞出很多子进程。理论上最好所有子进程共享同一个线程池而不是每个进程单独拥有一个线程池。

import asyncio, time, os 
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor()

def sync_call(x):
    time.sleep(0.1)
    return x+1

async def main():
    loop = asyncio.get_running_loop()
    while True:
        res = await asyncio.gather(*(loop.run_in_executor(executor, sync_call, x) for x in range(100)))
        assert res == [_ for _ in range(1,101)]
        print(True)
        
if __name__ == '__main__':
    for _ in range(16):
        pid = os.fork()
        if pid == 0:
            break
    asyncio.run(main())

简化下来大概是这样的代码,其中线程池是自己几年前写的,当时确保了在单进程内使用多线程的情况下是绝对安全的,现在已经看不懂代码了,怎样才能知道多进程的情况下是否能确保安全呢?

上文这个代码拿来跑当测试,倒是也没报错,但是我怎么知道它是不是刚好没遇到特殊情况所以没坑。。

2884 次点击
所在节点    Python
12 条回复
janus77
2022-04-19 13:15:50 +08:00
太经典了,自己写的代码过一段时间就看不懂了
gfreezy
2022-04-19 13:28:48 +08:00
进程之间不共享(好像除了 fd 以外的)任何东西,包括线程。
fcfangcc
2022-04-19 13:41:26 +08:00
进程之间应该是无法共享线程池。为什么要 fork 多个子进程,直接启动多个 task 不就好了吗
fcfangcc
2022-04-19 13:43:01 +08:00
asyncio.wait([main() for i in range(16)])
qbqbqbqb
2022-04-19 13:56:03 +08:00
用多进程的话,传递给子进程 target 函数的参数和传回来的返回值必须是能用 pickle 序列化、反序列化的,或者是 multiprocessing 库里面的 Queue, Value, Array 之类的专用的进程间通信工具。不能随便传递其它对象。

另外就是子进程也不能随便用全局变量、全局对象(常量可以用,有状态的对象慎用),因为子进程里全局对象的初始状态是有区别的,而且修改之后也不会同步到其它进程。

Python 多进程有三种模式 fork, spawn, forkserver:
1 ) fork 模式子进程会继承此时主进程的状态(相当于当前 Python 分裂成两个,其中主进程继续执行当前函数,子进程跳到 target 函数)
2 ) spawn 和 forkserver 模式子进程为程序刚初始化后的状态(相当于重新启动了一个 Python import 了所有库,然后不执行__main__直接跳到 target 函数开始执行)。

Linux 默认 fork ,Mac py3.8 以后默认 spawn, Windows 只支持 spawn 。
lolizeppelin
2022-04-19 14:03:39 +08:00
你这需求老老实实写线程. 协程都别写

觉得 python 线程不行要么换语言要么自己写 c 库

什么跨进程共享线程池都出来了
qbqbqbqb
2022-04-19 14:13:32 +08:00
@qbqbqbqb #5 是针对 Multiprocessing 多进程的。os.fork()就简单粗暴很多,子进程继承 fork 时的状态,之后对象状态的改变就不共享了,所以后来创建的线程池肯定是每个进程一个。想要统一池子还不如直接用 Multiprocessing 里的进程池。
cyrbuzz
2022-04-19 14:22:28 +08:00
多进程共享同一个线程即使可以的话优势在什么地方?这是为了解决什么问题= =。
lyz1990
2022-04-19 14:25:51 +08:00
进程能共享线程池么?
xuanbg
2022-04-19 15:49:18 +08:00
多实例化几个对象不行么。。。非要跨什么进程。
lolizeppelin
2022-04-19 16:38:28 +08:00
@qbqbqbqb
Multiprocessing 就是个大坑,读下 Multiprocessing 代码就知道了

Multiprocessing 就适合跑下简单业务,稍微复杂点的拿 Multiprocessing 跑简直找死
LeeReamond
2022-04-19 20:24:22 +08:00
@qbqbqbqb
@fcfangcc
@janus77
@gfreezy
@lolizeppelin
@xuanbg 异步是为了让过程调用受网络管理,需求是 CPU 密集型任务所以使用 fork 多进程,楼上老哥说得对,我试了一下好像真的除了 fd 以外不共享任何数据,跟我记忆中有些偏差(我印象中不特意创建进程间可共享内存也有同指向发生,试了下好像除了虚拟内存表以外物理内存表也全拷贝了,完全的互不相干。。)


关于代码写完过几年看不懂的问题,因为是开源项目其实当时还写了蛮详细的注释的,只不过是用英文写的,现在看注释一大坨一大坨像看论文一样实在不想看。可能这个故事教育我们就是不要好面子写英文,外国人看不看得懂不是最重要的,自己能看懂才是。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/847868

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX