解决Python项目部署的时候有个对象吃GPU的问题

摘要:在zdppy_api多个woker部署的时候,有多个进程,其中有一个类,在创建实例的时候会很吃GPU,但是实际上这个类全局只需要创建一次就行,而多进程中,这个类会被创建多次,也就是会多次消耗GPU,从而导致容器在启动时就崩溃,无法正常启动。

问题描述

在zdppy_api多个woker部署的时候,有多个进程,其中有一个类,在创建实例的时候会很吃GPU,但是实际上这个类全局只需要创建一次就行,而多进程中,这个类会被创建多次,也就是会多次消耗GPU,从而导致容器在启动时就崩溃,无法正常启动。

#智启新篇计划#

解决方案

让这个吃GPU的类,变成单例,这样的话,全局就只有一个对象,只会被创建一次。

参考代码

import threading

classSingleton:
_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__方法用于创建类的实例。

以下是一些方法的使用示例:classGreeting:
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 threading

classA:
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进行了改造。

来源:小唐科技频道

相关推荐