需求,一个异步脚本里大量使用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())
简化下来大概是这样的代码,其中线程池是自己几年前写的,当时确保了在单进程内使用多线程的情况下是绝对安全的,现在已经看不懂代码了,怎样才能知道多进程的情况下是否能确保安全呢?
上文这个代码拿来跑当测试,倒是也没报错,但是我怎么知道它是不是刚好没遇到特殊情况所以没坑。。
1
janus77 2022-04-19 13:15:50 +08:00
太经典了,自己写的代码过一段时间就看不懂了
|
2
gfreezy 2022-04-19 13:28:48 +08:00
进程之间不共享(好像除了 fd 以外的)任何东西,包括线程。
|
3
fcfangcc 2022-04-19 13:41:26 +08:00
进程之间应该是无法共享线程池。为什么要 fork 多个子进程,直接启动多个 task 不就好了吗
|
4
fcfangcc 2022-04-19 13:43:01 +08:00
asyncio.wait([main() for i in range(16)])
|
5
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 。 |
6
lolizeppelin 2022-04-19 14:03:39 +08:00 1
你这需求老老实实写线程. 协程都别写
觉得 python 线程不行要么换语言要么自己写 c 库 什么跨进程共享线程池都出来了 |
7
qbqbqbqb 2022-04-19 14:13:32 +08:00
@qbqbqbqb #5 是针对 Multiprocessing 多进程的。os.fork()就简单粗暴很多,子进程继承 fork 时的状态,之后对象状态的改变就不共享了,所以后来创建的线程池肯定是每个进程一个。想要统一池子还不如直接用 Multiprocessing 里的进程池。
|
8
cyrbuzz 2022-04-19 14:22:28 +08:00
多进程共享同一个线程即使可以的话优势在什么地方?这是为了解决什么问题= =。
|
9
lyz1990 2022-04-19 14:25:51 +08:00
进程能共享线程池么?
|
10
xuanbg 2022-04-19 15:49:18 +08:00
多实例化几个对象不行么。。。非要跨什么进程。
|
11
lolizeppelin 2022-04-19 16:38:28 +08:00
@qbqbqbqb
Multiprocessing 就是个大坑,读下 Multiprocessing 代码就知道了 Multiprocessing 就适合跑下简单业务,稍微复杂点的拿 Multiprocessing 跑简直找死 |
12
LeeReamond OP @qbqbqbqb
@fcfangcc @janus77 @gfreezy @lolizeppelin @xuanbg 异步是为了让过程调用受网络管理,需求是 CPU 密集型任务所以使用 fork 多进程,楼上老哥说得对,我试了一下好像真的除了 fd 以外不共享任何数据,跟我记忆中有些偏差(我印象中不特意创建进程间可共享内存也有同指向发生,试了下好像除了虚拟内存表以外物理内存表也全拷贝了,完全的互不相干。。) 关于代码写完过几年看不懂的问题,因为是开源项目其实当时还写了蛮详细的注释的,只不过是用英文写的,现在看注释一大坨一大坨像看论文一样实在不想看。可能这个故事教育我们就是不要好面子写英文,外国人看不看得懂不是最重要的,自己能看懂才是。。 |