摘要:在zdppy_api多个woker部署的时候,有多个进程,其中有一个类,在创建实例的时候会很吃GPU,但是实际上这个类全局只需要创建一次就行,而多进程中,这个类会被创建多次,也就是会多次消耗GPU,从而导致容器在启动时就崩溃,无法正常启动。
问题描述
在zdppy_api多个woker部署的时候,有多个进程,其中有一个类,在创建实例的时候会很吃GPU,但是实际上这个类全局只需要创建一次就行,而多进程中,这个类会被创建多次,也就是会多次消耗GPU,从而导致容器在启动时就崩溃,无法正常启动。
#智启新篇计划#
解决方案
让这个吃GPU的类,变成单例,这样的话,全局就只有一个对象,只会被创建一次。
参考代码
import threadingclassSingleton:
_instance = None
_lock = threading.Lock
def__new__(cls, *args, **kwargs):
with cls._lock:
if cls._instance isNone:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
def__init__(self):
print("aaaaaaaaaaaaa")
# 使用
instance1 = Singleton
instance2 = Singleton
print(instance1 is instance2) # 输出 True
单例能够保证_instance只有一个,也就是instance1 is instance2的结果是True,但是不会保证__init__只执行一次,__init__每次创建对象都会执行。所以控制台,会输出两次print的结果。在Python中,和__init__是两个特殊的方法,它们都在创建类的新实例时被调用,但它们的用途和行为有所不同。
__new__
__new__是一个静态方法,也就是说它不需要在实例上被调用,它的作用是创建一个新实例。__new__必须返回一个实例对象,这个对象可以是类本身的实例,也可以是其他类的实例。__new__通常用于继承不可变类型(如int、str、tuple等)或者实现单例模式。如果__new__方法在派生类中被重写,那么它必须调用父类的__new__方法,并且通常使用super.__new__(cls, ...)来完成。__init__
__init__是一个实例方法,它在__new__之后被调用,用于初始化新创建的实例。__init__不返回任何值,或者更准确地说,它返回None。__init__用于设置实例变量和执行其他初始化操作。如果__init__在派生类中被重写,它不需要(也不应该)调用父类的__init__方法,除非有特定的初始化需要。区别
调用顺序:__new__首先被调用以创建实例,然后__init__被调用以初始化这个实例。返回值:__new__必须返回一个对象,而__init__不返回任何值。用途:__new__用于控制对象的创建过程,而__init__用于对象的初始化。示例
classMyClass:def__new__(cls, *args, **kwargs):
print("Creating an instance of MyClass")
instance = super.__new__(cls)
return instance
def__init__(self, value):
print("Initializing the instance")
self.value = value
# 创建实例
obj = MyClass(10)
在这个例子中,当你创建MyClass的一个实例时,首先会打印出"Creating an instance of MyClass",然后打印出"Initializing the instance"。这展示了__new__的调用顺序和它们在对象创建过程中的不同作用。总结来说,__new__负责创建对象,而__init__负责初始化对象。理解这两个方法的区别对于深入掌握Python面向对象编程非常重要。
抽象可能有问题代码
A是需要消耗GPU的那个类,B是调用A的类。
classA:def__init__(self):
print("a need gpu")
deffa(self):
print("fa")
classB:
def__init__(self):
self.a = A
def__call__(self):
self.a.fa
print("ba....")
b = B
b
b1 = B
b1
在原本代码中,B里面的a并不是单例的,所以每次执行,都会创建新的对象,每次创建新的对象,都会消耗较高GPU,一旦对象创建过多,GPU不够,程序就会崩溃。
__call____call__ 方法是一个特殊方法,它允许一个实例变得可调用,即可以像函数那样被调用。当一个类的实例被用作函数时,Python会自动调用这个方法。__call__使实例可调用:
当你想要让你的类的实例表现得像一个函数时,你可以实现__call__方法。这在创建需要延迟计算或需要缓存结果的自定义对象时非常有用。
工厂模式:__call__方法常用于工厂模式,其中类的实例负责返回其他类的实例。
装饰器:
在装饰器模式中,__call__方法用于包装函数,并在调用被装饰函数前后执行额外的代码。
元类:
在创建类的类(即元类)时,__call__方法用于创建类的实例。
def__init__(self, name):
self.name = name
def__call__(self):
returnf"Hello, {self.name}!"
g = Greeting("Kimi")
print(g) # 输出: Hello, Kimi!
在这个例子中,Greeting类的实例g表现得像一个函数,当调用g时,实际上是调用了g__call__方法。
示例2:工厂模式
classGreeter:def__call__(cls, name):
return cls.get_greeting(name)
@staticmethod
defget_greeting(name):
returnf"Hello, {name}!"
# 使用
greeting = Greeter
print(greeting("Kimi")) # 输出: Hello, Kimi!
在这个例子中,Greeter类有一个__call__方法,它接受一个名字并返回一个问候语。
示例3:装饰器
defmy_decorator(func):defwrapper(*args, **kwargs):
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
defsay_hello(name):
print(f"Hello, {name}!")
say_hello("Kimi")
虽然这个例子没有直接使用__call__,但my_decorator函数返回的wrapper函数实际上是一个可调用对象,它在被调用时执行额外的代码。这是装饰器模式的一个典型应用,方法在内部被用来使可调用。__call__方法是Python中一个非常强大的特性,它提供了极大的灵活性,允许你以函数的方式使用对象。
最终的解决方案
import threadingclassA:
def__init__(self):
print("a need gpu")
deffa(self):
print("fa")
classB:
_a = None
_lock = threading.Lock
def__init__(self):
with B._lock:
if B._a isNone:
B._a = A
def__call__(self):
self._a.fa
print("ba....")
b = B
b
b1 = B
b1
这里核心是对B进行了改造。
来源:小唐科技频道
免责声明:本站系转载,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与本站联系,我们将在第一时间删除内容!