python-3.x 当使用多线程设计再次调用方法时,线程只能启动一次

cwxwcias  于 2023-06-07  发布在  Python
关注(0)|答案(2)|浏览(219)

我创建了一个deco来用相同的方法在多线程中运行一堆东西

import threading
from functools import wraps

class ThreadingMask:
    '''
    a deco to run dymastic method which has a list as first arg
    will separate the list into several lists (according to maxThreads) and run them with the method in multi-threads
    there is an example under defination
    '''
    def __init__(self, maxThreads: int = 64):
        self.maxThreads = maxThreads
        self.groups: list[list] = []
        self.threads = []

    def separate_groups(self, items):
        # init groups
        i = 0
        while i < self.maxThreads:
            self.groups.append([])
            i += 1
        i = 0
        # separate groups
        for item in items:
            if i == self.maxThreads:
                i = 0
            self.groups[i].append(item)
            i += 1

    def __call__(self, func):
        @wraps(func)
        def group_run(*args, **kwargs):
            if type(args[1]) is list:
                self.separate_groups(args[1])
            for group in self.groups:
                argsSep = list(args)
                argsSep[1] = group
                argsSep = tuple(argsSep)
                th = threading.Thread(target=func, args=(argsSep), kwargs=(kwargs))
                self.threads.append(th)
            for th in self.threads:
                th.start()
            for th in self.threads:
                th.join()
        return group_run
import time
class Test:
    '''just a sample'''

    def __init__(self):
        self.result = []
        self.lock = threading.Lock()

    @ThreadingMask()
    def runs(self, items, typeLimit, isNot="a"):
        for item in items:
            def get_matched():
                time.sleep(1)
                if type(item) is typeLimit and str(item) != str(isNot):
                    self.lock.acquire()
                    self.result.append(item)
                    self.lock.release()
            get_matched()

if __name__ == "__main__":
    start = time.time()
    a = Test()
    bunchOfItems: list = ["d", None, True, "", 1, 2, 3, "a", "b", "c", "this", "e", "d", 13, "p", 0.1, 11]
    a.runs(bunchOfItems, str, isNot="b")
    a.result.sort()
    print(a.result)
    b = Test()
    b.runs(bunchOfItems, str, isNot="b")

但是RuntimeError:threads can be only started once occurs when the second time call the method with this deco有没有一种方法可以在第二次调用该方法时启动不同的线程,而不是第一次调用的线程?

aemubtdh

aemubtdh1#

你必须在装饰器对象(ThreadingMask)上创建一个新的线程示例,以在单独的线程中运行,而不是尝试启动同一个线程两次,这是不可能的(你使用的是同一个self.threads对象,你应该在函数上创建新的线程)。
修改的代码示例:

import threading
import time
from functools import wraps

class ThreadingMask:
    '''
    a deco to run dynamic method which has a list as the first arg
    will separate the list into several lists (according to maxThreads) and run them with the method in multi-threads
    there is an example under definition
    '''
    def __init__(self, maxThreads: int = 64):
        self.maxThreads = maxThreads

    def separate_groups(self, items):
        # init groups
        groups = [[] for _ in range(self.maxThreads)]
        # separate groups
        for i, item in enumerate(items):
            groups[i % self.maxThreads].append(item)
        return groups

    def __call__(self, func):
        @wraps(func)
        def group_run(*args, **kwargs):
            if type(args[1]) is list:
                groups = self.separate_groups(args[1])
            else:
                groups = [[]]
            threads = []
            for group in groups:
                args_sep = list(args)
                args_sep[1] = group
                args_sep = tuple(args_sep)
                th = threading.Thread(target=func, args=args_sep, kwargs=kwargs)
                threads.append(th)
            for th in threads:
                th.start()
            for th in threads:
                th.join()
        return group_run

class Test:
    '''just a sample'''

    def __init__(self):
        self.result = []
        self.lock = threading.Lock()

    @ThreadingMask()
    def runs(self, items, typeLimit, isNot="a"):
        for item in items:
            def get_matched():
                time.sleep(1)
                if type(item) is typeLimit and str(item) != str(isNot):
                    with self.lock:
                        self.result.append(item)
            get_matched()

if __name__ == "__main__":
    start = time.time()
    a = Test()
    b = Test()

    bunchOfItems: list = ["d", None, True, "", 1, 2, 3, "a", "b", "c", "this", "e", "d", 13, "p", 0.1, 11]
    a.runs(bunchOfItems, str, isNot="b")
    b.runs(bunchOfItems, str, isNot="b")

    a.result.sort()
    b.result.sort()
    print(a.result)
    print(b.result)
ljsrvy3e

ljsrvy3e2#

我想我得到了答案,我没有在每次调用时重置self.threads = []。
根据[https://stackoverflow.com/questions/27575954/python-decorator-with-arguments-only-called-once],它应该在 Package 的func group_run下

def __call__(self, func):
    @wraps(func)
    def group_run(*args, **kwargs):
        self.threads = []
        if type(args[1]) is list:
            self.separate_groups(args[1])
        for group in self.groups:
            argsSep = list(args)
            argsSep[1] = group
            argsSep = tuple(argsSep)
            th = threading.Thread(target=func, daemon=True, args=(argsSep), kwargs=(kwargs))
            self.threads.append(th)
        for th in self.threads:
            th.start()
        for th in self.threads:
            th.join()
    return group_run

相关问题