Python:如何停止Threading/Multiprocessing占用100%的CPU?

4

我有一段代码,每秒钟从7个设备中读取数据,而且要无限循环。每次循环都会创建一个线程,启动7个进程。在每个进程完成后,程序会等待1秒钟再重新开始。以下是代码片段:

def all_thread(): #function that handels the threading
    thread = threading.Thread(target=all_process) #prepares a thread for the devices
    thread.start() #starts a thread for the devices

def all_process(): #function that prepares and runs processes
    processes = [] #empty list for the processes to be stored
    while len(gas_list) > 0: #this gaslist holds the connection information for my devices
        for sen in gas_list: #for each sen(sensor) in the gas list
            proc = multiprocessing.Process(target=main_reader, args=(sen, q)) #declaring a process variable that sends the gas object, value and queue information to reading function
            processes.append(proc) #adding the process to the processes list
            proc.start() #start the process
        for sen in processes: #for each sensor in the processes list
            sen.join() #wait for all the processes to complete before starting again
        time.sleep(1) #wait one second

然而,这会使用我的CPU的100%。这是由于线程和多进程设计还是糟糕的编码?有没有办法限制CPU使用率?谢谢!
更新:
评论中提到了main_reader()函数,所以我将其放入问题中。它只是读取每个设备,获取所有数据并将其附加到列表中。然后,该列表被放入队列中以在tkinter GUI中显示。
def main_reader(data, q): #this function reads the device which takes less than a second
    output_list = get_registry(data) #this function takes the device information, reads the registry and returns a list of data
    q.put(output_list) #put the output list into the queue

在Python中启动一个进程意味着启动Python解释器的新实例,这意味着在您的情况下,您有7个独立的解释器在4-8个核心上运行(我假设)。当一个新的解释器被生成时,它会从父进程继承一些资源,以便能够完成它的工作,这意味着生成一个新进程可能会非常缓慢和昂贵。您的进程正在运行一个名为“main_reader()”的函数,根据名称,我认为您正在做的工作主要是IO相关的。您是否尝试过启动一堆守护进程而不是所有这些进程? - orangeInk
@orangeInk,是的,所有都是输入输出。main_reader()函数读取设备并准备显示数据。我没有使用过守护进程。目前,我正在使用多进程技术来保持tkinter GUI在设备工作时不会冻结。那么,使用守护进程也可以实现此目的吗? - GreenSaber
你也可以尝试使用multiprocessing.Pool。它只会启动一次子进程,然后你可以不断地向其中添加任务。如果你的意图仅仅是让工作池有更多的任务要做,那么你就不需要等待它们完成并关闭它们。这将减少进程创建的开销,特别是当主读取器预计在非常短的时间内完成时,这将非常有用。 - Hannu
如果运行main_reader()只需要几分之一秒的时间,移动processes = []可能会有所帮助。如果你不这样做,并且让进程列表不断增长,它将快速增长,并且你用来加入进程的循环将首先尝试加入根本没有运行的成千上万个进程,直到最终命中实际正在运行的最新几个。 - Hannu
@Hannu 我刚把它移到 while 循环的第一行,但我的 CPU 立即飙升到 100%。 - GreenSaber
显示剩余6条评论
1个回答

2

根据您在评论中提到的,您的主读取器只需要运行几分之一秒,这意味着进程创建开销可能导致问题。

以下是一个使用 multiprocessing.Pool 的示例。它创建了一个工作进程池并将您的任务提交给它们。进程仅在第一次启动时启动,如果这是无限循环,则永远不会关闭或加入。如果您想要关闭进程池,可以通过加入和关闭来实现(请参阅文档)。

from multiprocessing import Pool, Manager
from time import sleep
import threading
from random import random

gas_list = [1,2,3,4,5,6,7,8,9,10]

def main_reader(sen, rqu):
    output = "%d/%f" % (sen, random())
    rqu.put(output)


def all_processes(rq):
    p = Pool(len(gas_list) + 1)
    while True:
        for sen in gas_list:
            p.apply_async(main_reader, args=(sen, rq))

        sleep(1)

m = Manager()
q = m.Queue()
t = threading.Thread(target=all_processes, args=(q,))
t.daemon = True
t.start()

while True:
    r = q.get()
    print r

如果这不起作用,你需要更深入地挖掘。我建议首先将无限循环中的睡眠时间增加到10秒甚至更长。这样可以让你监视程序的行为。如果CPU短暂飙升然后在10秒左右稳定下来,那么问题就出现在你的main_reader中。如果仍然保持100%,那么你的问题必须在其他地方。
你的问题是否可能根本不在程序的这个部分?你好像在一个线程中启动了所有操作,这表明你的主程序正在做其他事情。是不是这个其他事情导致了CPU高峰?

我现在要测试一下,然后再回复你。我启动了一个线程来分离Tkinter的主循环和我需要处理设备的工作。 - GreenSaber
你可以尝试在不使用tkinter循环的情况下运行程序。只需在启动程序的这部分后面不要开始它,而是在sleep(bigtime)中等待。虽然你的应用程序此时并没有做太多事情,但如果它能够在运行所有这些后台任务的同时表现良好,那么你至少知道问题不在代码的这部分。 - Hannu
你的意思是什么?main_reader没有运行吗?它运行了但没有完成吗?队列里面没有东西吗?(你使用的是multiprocessing中的Queue,对吧?) - Hannu
好的,我大部分已经解决了,现在唯一的问题是,在每次sleep之后,似乎并不是gas_list中的每个项目都能到达main_reader - GreenSaber
让我们在聊天中继续这个讨论。点击此处进入聊天室 - GreenSaber
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接