python-3.x 如何用构造函数和析构函数实现线程计数的单例?

rbpvctlc  于 2023-08-08  发布在  Python
关注(0)|答案(1)|浏览(113)

我有多个线程来构造一个参数化的对象,并在其上调用start和stop。问题是,每个参数的基础服务应该只在第一次启动时启动,并且只在最后一次停止时停止一次。我尝试了以下方法:

# /usr/bin/env python3
import multiprocessing.pool
import random
import threading
import time
import uuid

def log(args):
    print(f"{threading.get_ident()}: {args}")

class Myobj:
    lock = threading.Lock()
    count = 0

    def __init__(self, name: str):
        self.name = name

    def log(self, args):
        log(f"{self.name}: {args}")

    def start(self):
        with self.lock:
            self.count += 1
            if self.count == 1:
                self.value = uuid.uuid1()
                self.log(f"starting {self.value}")
            self.log(f"ref up {self.count}")

    def stop(self):
        with self.lock:
            self.count -= 1
            if self.count == 0:
                self.log(f"stopping {self.value}")
            self.log(f"ref down {self.count}")

def thread(i):
    # references service with specific name
    myobj = Myobj(f"name{i % 2}")
    # only the first thread that gets here should start the service with that name
    myobj.start()
    # some_computation()
    time.sleep(random.uniform(0, 2))
    # only the last thread that gets here should stop the service with that name
    myobj.stop()

size = 6
with multiprocessing.pool.ThreadPool(size) as tp:
    tp.map(thread, range(size))

字符串
但是,计数器不是唯一的:

140385706604224: name0: starting ab142d3c-2ebd-11ee-9d78-901b0e12b878
140385706604224: name0: ref up 1
140385698211520: name1: starting ab143264-2ebd-11ee-a67e-901b0e12b878
140385698211520: name1: ref up 1
140385689818816: name0: starting ab1435fc-2ebd-11ee-99f3-901b0e12b878
140385689818816: name0: ref up 1
140385681426112: name1: starting ab1439b2-2ebd-11ee-932c-901b0e12b878
140385681426112: name1: ref up 1
140385673033408: name0: starting ab143d36-2ebd-11ee-9959-901b0e12b878
140385673033408: name0: ref up 1
140385664640704: name1: starting ab1443f8-2ebd-11ee-a1b9-901b0e12b878
140385664640704: name1: ref up 1
140385673033408: name0: stopping ab143d36-2ebd-11ee-9959-901b0e12b878
140385673033408: name0: ref down 0
140385706604224: name0: stopping ab142d3c-2ebd-11ee-9d78-901b0e12b878
140385706604224: name0: ref down 0
140385689818816: name0: stopping ab1435fc-2ebd-11ee-99f3-901b0e12b878
140385689818816: name0: ref down 0
140385681426112: name1: stopping ab1439b2-2ebd-11ee-932c-901b0e12b878
140385681426112: name1: ref down 0
140385664640704: name1: stopping ab1443f8-2ebd-11ee-a1b9-901b0e12b878
140385664640704: name1: ref down 0
140385698211520: name1: stopping ab143264-2ebd-11ee-a67e-901b0e12b878
140385698211520: name1: ref down 0


理想情况下,我应该看到name0name1开始和停止行打印一次,如下所示,服务name0和服务name1启动,然后停止。name0name1服务之间的顺序无关。所以只能输出如下所示:

140385706604224: name0: starting ab142d3c-2ebd-11ee-9d78-901b0e12b878
 140385698211520: name1: starting ab143264-2ebd-11ee-a67e-901b0e12b878
 140385681426112: name0: stopping ab142d3c-2ebd-11ee-9d78-901b0e12b878
 140385664640704: name1: stopping ab143264-2ebd-11ee-a67e-901b0e12b878


什么是一个好的设计来实现这样一个类,它可以启动和停止具有多个线程的唯一命名服务?这可以作为泛型类实现吗?

fnatzsnv

fnatzsnv1#

只要让计数器变成一个可变对象,它就应该按照你想要的方式运行。
现在,类变量count是一个int,一个不可变类型。如果这是一个可变类型,那么它将按照您所期望的方式在示例之间共享它的值。

class Foo:
    x = 0
    def incr(self):
        self.x += 1

a = Foo()
b = Foo()
a.incr()
print(Foo.x, a.x, b.x)  # change is not shared

字符串
但是如果你使用一个可变对象,比如collections.Counter,这就像你所期望的那样工作:

from collections import Counter
class Foo:
    x = Counter()
    def incr(self):
        self.x['x'] += 1
a = Foo()
b = Foo()
a.incr()
print(Foo.x, a.x, b.x)  # x reference is the same for all objects


将此应用于您的场景:

import multiprocessing.pool
import random
import threading
import time
import uuid
from collections import Counter

def log(args):
    print(f"{threading.get_ident()}: {args}")

class Myobj:
    lock = threading.Lock()
    counts = Counter()
    values = dict()

    def __init__(self, name: str):
        self.name = name

    def log(self, args):
        log(f"{self.name}: {args}")

    @property
    def count(self):
        return self.counts[self.name]
        
    @count.setter
    def count(self, new_count):
        self.counts[self.name] = new_count
        
    @property
    def value(self):
        return self.values[self.name]
        
    @value.setter
    def value(self, new_value):
        self.values[self.name] = new_value
        
    def start(self):
        with self.lock:
            self.count += 1
            if self.count == 1:
                self.value = uuid.uuid1()
                self.log(f"starting {self.value}")
            self.log(f"ref up {self.count}")

    def stop(self):
        with self.lock:
            self.count -= 1
            if self.count == 0:
                self.log(f"stopping {self.value}")
            self.log(f"ref down {self.count}")

def thread(i):
    # references service with specific name
    myobj = Myobj(f"name{i % 2}")
    # only the first thread that gets here should start the service with that name
    myobj.start()
    # some_computation()
    time.sleep(random.uniform(0, 2))
    # only the last thread that gets here should stop the service with that name
    myobj.stop()

size = 6
with multiprocessing.pool.ThreadPool(size) as tp:
    tp.map(thread, range(size))
6089797632: name0: starting 4bb3694c-2ec8-11ee-8745-9d96bc9d9be2
6089797632: name0: ref up 1
6091517952: name1: starting 4bb3bf28-2ec8-11ee-8745-9d96bc9d9be2
6091517952: name1: ref up 1
6092091392: name0: ref up 2
6093238272: name1: ref up 2
6094958592: name0: ref up 3
6095532032: name1: ref up 3
6091517952: name1: ref down 2
6094958592: name0: ref down 2
6095532032: name1: ref down 1
6089797632: name0: ref down 1
6093238272: name1: stopping 4bb3bf28-2ec8-11ee-8745-9d96bc9d9be2
6093238272: name1: ref down 0
6092091392: name0: stopping 4bb3694c-2ec8-11ee-8745-9d96bc9d9be2
6092091392: name0: ref down 0

的字符串

相关问题