Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 3中的多线程递归函数 背景:_Python_Python 3.x_Recursion_Python Multithreading_Variadic - Fatal编程技术网

Python 3中的多线程递归函数 背景:

Python 3中的多线程递归函数 背景:,python,python-3.x,recursion,python-multithreading,variadic,Python,Python 3.x,Recursion,Python Multithreading,Variadic,我正在编写电信网络发现脚本,该脚本由crontab在linux上运行。它使用初始网络节点的种子文件,连接到它们,获取所有邻居,然后连接到这些邻居,依此类推。典型的递归。 为了加速整个过程,我使用了带信号量的多线程,所以我只有一定数量的正在运行的线程,但有大量的已启动线程正在等待。在某个时刻,我遇到了linux的最大线程限制,因此脚本无法启动新线程 问题: 在追求一种设计时,这将允许这种递归的多线程,在我看来,这是一种多生产者/消费者混合场景。多个消费者也在生产 使用者从队列中获取项目并使用它,如

我正在编写电信网络发现脚本,该脚本由crontab在linux上运行。它使用初始网络节点的种子文件,连接到它们,获取所有邻居,然后连接到这些邻居,依此类推。典型的递归。 为了加速整个过程,我使用了带信号量的多线程,所以我只有一定数量的正在运行的线程,但有大量的已启动线程正在等待。在某个时刻,我遇到了linux的最大线程限制,因此脚本无法启动新线程

问题: 在追求一种设计时,这将允许这种递归的多线程,在我看来,这是一种多生产者/消费者混合场景。多个消费者也在生产

使用者从队列中获取项目并使用它,如果有任何结果,则将每个结果再次返回到队列中

为了让它变得更好,我想创建一个设计模式,它可以用于任何类型的递归函数,换句话说,可以用于任何arg和kwarg

我对这个函数的期望是,我传递它所需的任何变量组合(args,kwargs),并得到参数的返回列表,我可以在其他递归中再次传递给它

问题:
  • 除了我使用的方法之外,还有其他更好的方法来处理从函数return获取args和kwargs吗?我基本上创建了一个元组(args,kwargs)(tuple(),dict()),func返回该元组,然后Worker将其拆分为args,kwargs。理想情况是根本不需要创建那个元组

  • 你对这个设计还有什么改进建议吗

衷心感谢


当前代码:
我强烈怀疑你的线程池没有给你带来更多的性能,反而可能会损害性能。就实际执行python代码而言,您必须与全局解释器锁抗衡,这使得一次只能有一个线程执行python代码。您能否在您的版本和代码外观之间同步执行一些基准测试?这里最好的设计可能就是不去做它。要查看工作代码,请查看而不是查看StackOverflow@EthanMcCue因为我正在进行ssh连接并通过它提取数据,这需要时间,类似于示例中的随机睡眠,这就是为什么即使是多线程也能为我节省大量时间,并将脚本速度提高至少五倍。您是对的,如果它只在脚本本身上运行,那么它的效率不会更高。即使是GIL也不会困扰我,因为处理数据的时间对于获取数据的时间来说是微不足道的。如上所述,这应该转移到代码审查中,但是像asyncio这样的东西可能会有所帮助。
#!/usr/bin/env python3

from queue import Queue, Empty
from threading import Thread
from time import sleep
from random import choice, random


class RecursiveWorkerThread(Thread):

    def __init__(self, name, pool):
        Thread.__init__(self)
        self.name = name
        self.pool = pool
        self.tasks = pool.tasks
        self.POISON = pool.POISON
        self.daemon = False
        self.result = None
        self.start()

    def run(self):
        print(f'WORKER {self.name} - is awake.')
        while True:
            if not self.tasks.empty():
                # take task from queue
                try:
                    func, f_args, f_kwargs = self.tasks.get(timeout=1)

                    # check for POISON
                    if func is self.POISON:
                        print(f'WORKER {self.name} - POISON found. Sending it back to queue. Dying...')
                        self.pool.add_task(self.POISON)
                        break

                    # try to perform the task on arguments and get result
                    try:
                        self.result = func(*f_args, **f_kwargs)
                    except Exception as e:
                        print(e)

                    # recursive part, add results to queue
                    print(f'WORKER {self.name} - FUNC: ({func.__name__}) IN: (args: {f_args}, kwargs: {f_kwargs}) OUT: ({self.result}).')
                    for n_args, n_kwargs in self.result:
                        self.pool.add_task(func, *n_args, **n_kwargs)

                    # mark one task done in queue
                    self.tasks.task_done()
                except Empty:
                    pass
            sleep(random())


class RecursiveThreadPool:

    def __init__(self, num_threads):
        self.tasks = Queue()
        self.POISON = object()

        print('\nTHREAD_POOL - initialized.\nTHREAD_POOL - waking up WORKERS.')

        self.workers = [RecursiveWorkerThread(name=str(num), pool=self) for num in range(num_threads)]

    def add_task(self, func, *args, **kwargs):
        if func is not self.POISON:
            print(f'THREAD_POOL - task received: [func: ({func.__name__}), args: ({args}), kwargs:({kwargs})]')
        else:
            print('THREAD_POOL - task received: POISON.')
        self.tasks.put((func, args, kwargs))

    def wait_for_completion(self):
        print('\nTHREAD_POOL - waiting for all tasks to be completed.')

        self.tasks.join()

        print('\nTHREAD_POOL - all tasks have been completed.\nTHREAD_POOL - sending POISON to queue.')

        self.add_task(self.POISON)

        print('THREAD_POOL - waiting for WORKERS to die.')

        for worker in self.workers:
            worker.join()

        print('\nTHREAD_POOL - all WORKERS are dead.\nTHREAD_POOL - FINISHED.')


# Test part
if __name__ == '__main__':

    percentage = [True] * 2 + [False] * 8

    # example function
    def get_subnodes(node):
        maximum_subnodes = 2
        sleep(5 * random())
        result_list = list()
        for i in range(maximum_subnodes):
            # apply chance on every possible subnode
            if choice(percentage):
                new_node = node + '.' + str(i)
                # create single result
                args = tuple()
                kwargs = dict({'node': new_node})
                # append it to the result list
                result_list.append((args, kwargs))
        return result_list


    # 1) Init a Thread pool with the desired number of worker threads
    THREAD_POOL = RecursiveThreadPool(10)

    # 2) Put initial data into queue
    initial_nodes = 10
    for root_node in [str(i) for i in range(initial_nodes)]:
        THREAD_POOL.add_task(get_subnodes, node=root_node)

    # 3) Wait for completion
    THREAD_POOL.wait_for_completion()