python中的线程,因为GIL锁的问题,实际上同时执行的永远都是一个线程,不能充分发挥多核cpu的能力。多进程可以使用多个cpu,但是不能像多线程之间那么方便的共享变量和对象。比如说,在主线程创建了一个对象App,在子线程中检测App的数据发生了变化,从而执行某一动作。在多线程环境中,由于App是共享的,所以可以很方便的写出下面的代码。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import time
from threading import Thread
from multiprocessing import Process
class App:
def __init__(self):
self.set_data(0, 0)
def get_data(self):
return self.x, self.y
def set_data(self, x, y):
self.x = x
self.y = y
def RunApp(app):
while True:
data = app.get_data()
if data[0] > 10:
print("big data: ", data)
print("exit child thread")
break
else:
print("small data: ", data)
time.sleep(1)
if __name__ == "__main__":
app = App()
thrd = Thread(target=RunApp, args=(app,)) # using thread, app is shared
# thrd = Process(target=RunApp, args=(app,)) # using process, app is copied
thrd.start()
time.sleep(5) # child thead will print small data several times
app.set_data(20, 20) # child thread print big data and exit
thrd.join()
上面的代码输出如下:
small data: (0, 0)
small data: (0, 0)
small data: (0, 0)
small data: (0, 0)
small data: (0, 0)
big data: (20, 20)
exit child thread
主线程和子线程的app是同一个对象,所以在主线程修改了app的data,在线程可以检测到。
如果使用多进程的方式,讲上段代码中的Thead改为Process,那么由于对象不共享,子进程实际是复制了一个app,在主进程中的改变不会影响到子进程,因为程序就不会结束,一直输出“small data”。
在进程之间共享对象,参考python多进程文档,大概有三种方法,但是要共享自定义的Class,则只能勇士Manager这种方法。Manager是一个单独的服务端进程,对共享对象进行管理,其他进程通过socket的方式连接到Manager进程,获取对象,从而可以使用对象的公共方法。对上面的代码的略作修改,多进程的实现方式如下:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import time
from multiprocessing import Process
from multiprocessing.managers import BaseManager
class App:
def __init__(self):
self.set_data(0, 0)
def get_data(self):
return self.x, self.y
def set_data(self, x, y):
self.x = x
self.y = y
app = App()
class RobotManager(BaseManager):
pass
RobotManager.register("get_app", lambda: app)
m = RobotManager(address="fuckworld.com", authkey=b"Fuck, world")
m.start()
def RunApp():
m = RobotManager(address="fuckworld.com", authkey=b"Fuck, world")
m.connect()
app = m.get_app()
while True:
data = app.get_data()
if data[0] > 10:
print("big data: ", data)
print("exit child process")
break
else:
print("small data: ", data)
time.sleep(1)
if __name__ == "__main__":
p = Process(target=RunApp)
p.start()
app = m.get_app() # 必须使用这一句,不然的话,修改不是共享的
time.sleep(5) # child process will print small data several times
app.set_data(20, 20) # child process print big data and exit
p.join()
这种方式,服务端和客户端甚至可以运行在不同的机器上,大家可以试一试。当然address可能不能是”fuckworld.com"了。