Python 芹菜任务共享底层状态

Python 芹菜任务共享底层状态,python,celery,Python,Celery,我想在Python中为硬件资源上执行的操作实现一个调度程序。硬件资源封装在它自己的类中,对象包含状态信息,因此该对象应该只有一个实例。我试着用你的手来做这件事。我的任务基类是: from celery import Task class ObClTask(Task): def __init__(self): self.val = 0 def add(self, add_val): self.val += add_val retu

我想在Python中为硬件资源上执行的操作实现一个调度程序。硬件资源封装在它自己的类中,对象包含状态信息,因此该对象应该只有一个实例。我试着用你的手来做这件事。我的任务基类是:

from celery import Task

class ObClTask(Task):
    def __init__(self):
        self.val = 0

    def add(self, add_val):
        self.val += add_val
        return self.val

    def mult(self, mult_val):
        self.val *= mult_val
        return self.val
芹菜任务定义如下:

from celery import Celery
from obcl import ObClTask

@app.task(base=ObClTask)
def add(x):
    return add.add(x)

@app.task(base=ObClTask)
def mult(x):
    return mult.mult(x)
我用
--concurrency=1
启动
芹菜
,以确保只产生一个工作者

然后,运行一个序列,如

add.delay(5)
add.delay(5)
mult.delay(2)
mult.delay(2)
将返回
5
10
0
0
,当我希望
5
10
20
40
,即
add
mult
在同一实例上运行时。我知道
add
mult
生成了
ObClTask
的不同子类。但是有没有一种方法可以满足我对芹菜的需求呢

编辑:这可能适用于类属性。但是这是一个反模式吗?

@app.task(base=whatever)
创建一个新的任务类,因此您最终得到两个不同的任务实例


我强烈建议您不要使用任务实例来持久化状态-这对于只读值或本地缓存(cf)都是可以的,但对于这种用例则不行。如果要在任务执行之间保持共享状态,请将状态保存在某个数据库中,在任务函数开始时读取并在完成时更新(不要忘记设置一些锁,以便一次只有一个任务可以访问此资源)。

我在寻找类似问题的解决方案时发现了您的问题。这就是我最后要做的。我使用RabbitMQ作为代理,使用Redis作为结果后端,但要根据您的情况进行调整:

文件obcl/obcl.py

class ObCl(object):
    def __init__(self):
        self.val = 0

    def add(self, add_val):
        self.val += add_val
        return self.val

    def mult(self, mult_val):
        self.val *= mult_val
        return self.val
from obcl.obcl import ObCl
from celery import Celery
from celery.signals import worker_shutting_down


app = Celery('obcl_tasks', broker='pyamqp://guest@localhost//', backend='redis://localhost/')
my_obcl = ObCl()


@app.task
def add(x):
    return my_obcl.add(x)


@app.task
def mult(x):
    return my_obcl.mult(x)

@worker_shutting_down.connect
def task_sent_handler(sig=None, how=None, exitcode=None, **kwargs):
  # Maybe close hardware resources when shutting down the worker?
  # I had to in my case, you might too.
  pass
文件obcl/obcl_tasks.py

class ObCl(object):
    def __init__(self):
        self.val = 0

    def add(self, add_val):
        self.val += add_val
        return self.val

    def mult(self, mult_val):
        self.val *= mult_val
        return self.val
from obcl.obcl import ObCl
from celery import Celery
from celery.signals import worker_shutting_down


app = Celery('obcl_tasks', broker='pyamqp://guest@localhost//', backend='redis://localhost/')
my_obcl = ObCl()


@app.task
def add(x):
    return my_obcl.add(x)


@app.task
def mult(x):
    return my_obcl.mult(x)

@worker_shutting_down.connect
def task_sent_handler(sig=None, how=None, exitcode=None, **kwargs):
  # Maybe close hardware resources when shutting down the worker?
  # I had to in my case, you might too.
  pass
正在从obcl模块的父文件夹运行芹菜。请确保使用
--concurrency=1
,以便只有一个实例工作进程控制硬件:

celery -A obcl.obcl_tasks worker --pool=solo --concurrency=1 --loglevel=info

 -------------- celery@localhost v4.2.2 (windowlicker)
---- **** -----
--- * ***  * -- Linux-4.18.0-16-generic-x86_64-with-Ubuntu-18.04-bionic 2019-03-24 17:10:03
-- * - **** ---
- ** ---------- [config]
- ** ---------- .> app:         obcl_tasks:0x7f443c0bc5c0
- ** ---------- .> transport:   amqp://guest:**@localhost:5672//
- ** ---------- .> results:     redis://localhost/
- *** --- * --- .> concurrency: 1 (solo)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
 -------------- [queues]
                .> celery           exchange=celery(direct) key=celery


[tasks]
  . obcl.obcl_tasks.add
  . obcl.obcl_tasks.mult

[2019-03-24 17:10:03,041: INFO/MainProcess] Connected to amqp://guest:**@127.0.0.1:5672//
[2019-03-24 17:10:03,049: INFO/MainProcess] mingle: searching for neighbors
[2019-03-24 17:10:04,070: INFO/MainProcess] mingle: all alone
[2019-03-24 17:10:04,086: INFO/MainProcess] celery@localhost ready.
现在,您可以调用您的任务,应该可以得到预期的结果

Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from obcl.obcl_tasks import add, mult
>>> test = add.delay(5)
>>> test.result
5
>>> test = add.delay(5)
>>> test.result
10
>>> test = mult.delay(2)
>>> test.result
20
>>> test = mult.delay(2)
>>> test.result
40

谢谢–在我的场景中,我只有一个worker,所以在给定的时间,无论如何只有一个任务应该访问资源。如果可能的话,我希望避免打开和关闭硬件资源,因为重新打开基本上需要硬件重置,我希望避免。