Как мне заставить питонов Queue.Queue работать быстрее? - PullRequest
0 голосов
/ 28 февраля 2019

Я работал над tkinter -GUI, из которого я запускаю долго работающие внешние скрипты, выходные данные которых затем записываются в GUI.Чтобы мой графический интерфейс не зависал во время работы сценариев, я использую threading -модуль.

Это сработало в принципе, но я столкнулся с проблемой, что мой GUI часто зависал.Полагаю, что из-за того, что сценарии выдавали вывод быстрее, чем он мог отображать,

Чтобы решить эту проблему, я теперь записываю вывод своих сценариев в очередь, используя Queue -модуль.Теперь он работает почти так, как задумано, но если у меня одновременно запущено много процессов (> 20), все они начинают работать очень медленно или, по крайней мере, выходные данные отображаются с задержкой по времени.Я предполагаю, что это потому, что очередь «не успевает» за выходными данными сценариев.

Это приводит меня к моему вопросу: есть ли способ заставить работу очереди работать быстрее, или, альтернативно, есть?способ заставить мою программу работать быстрее?

Вот минимальный пример моего кода.Вместо внешних сценариев я просто позволяю программе непрерывно считать:

import sys, os
import Tkinter as tk
from threading import Thread
import time
import Queue


class GUI:
    def __init__(self,master):
        self.master = master
        master.title('Threading Test')

        self.thread_queue = Queue.PriorityQueue()#Queue where all the outputs are lined up
        self.runprocess = [0 for i in range(30)]#These will be the theads, later

        #Dropdown menu that lets you select the output window
        self.options = tk.StringVar()
        self.options.set('1')
        self.numbers = [str(q) for q in range(1,30)]
        self.Option = tk.OptionMenu(master,self.options,*self.numbers,command=self.Select_Window)
        self.Option.pack(fill='x')

        #Button that starts function (in a thread)
        self.button = tk.Button(master,text='start',command = self.run_thread)
        self.button.pack()

        #Output windows
        self.Output_Frame = tk.Frame(master,width=800,height=100)
        self.Output_Frame.pack(fill='both',expand=1)
        self.Output_Frame_Pages = [0 for i in range(30)]
        self.Output_Fields = [0 for i in range(30)]
        self.Output_Scroll = [0 for i in range(30)]
        for q in range(len(self.Output_Frame_Pages)):
            self.Output_Frame_Pages[q] = tk.Frame(self.Output_Frame)
            self.Output_Frame_Pages[q].place(in_=self.Output_Frame,x=0,y=0,relwidth=1,relheight=1)
            self.Output_Fields[q] = tk.Text(self.Output_Frame_Pages[q],bg='white')
            self.Output_Fields[q].pack(fill='both',expand=1,side='left')
            self.Output_Fields[q].configure(state='disabled')
            self.Output_Scroll[q] = tk.Scrollbar(self.Output_Frame_Pages[q],command=self.Output_Fields[q].yview)
            self.Output_Scroll[q].pack(side='left',fill='y')
            self.Output_Fields[q]['yscrollcommand'] = self.Output_Scroll[q].set
        self.Output_Frame_Pages[0].lift()

        #Function that checks if there is something in the queue and then writes the output
        self.master.after(1,self.doWork())

    #Function that lets you chose the output window
    def Select_Window(self,ddmvar):
        self.Output_Frame_Pages[int(self.options.get())-1].lift()

    #Function that writes output
    def Write_Output(self,Message,Window):
        self.Output_Fields[Window-1].configure(state='normal')
        self.Output_Fields[Window-1].insert(tk.END,str(Message)+'\n')
        self.Output_Fields[Window-1].see(tk.END)
        self.Output_Fields[Window-1].configure(state='disabled')

    #Function that "does stuff"
    def run(self):
        i = 0
        aux = int(self.options.get())
        while True:
            i+=1
            self.thread_queue.put((i,aux))
            #print 'running'

    #Function that calls "run" in a thread, so GUI does not freeze
    def run_thread(self):
        aux = int(self.options.get())
        self.runprocess[aux-1] = Thread(target=self.run)
        self.runprocess[aux-1].daemon = True
        self.runprocess[aux-1].start()

    #Function that checks if there is something in the queue an then writes the output
    def doWork(self):
        try:
            self.Write_Output(*self.thread_queue.get(0))
            self.master.after(1,self.doWork)
        except Queue.Empty:
            self.master.after(1,self.doWork)



root = tk.Tk()
gui = GUI(root)
root.mainloop()

Другая проблема, которую я заметил, заключается в том, что мои потоки, похоже, продолжаются, даже если я закрываю графический интерфейс.Это видно, если я позволю своей функции не только записывать в GUI, но и позволить моей программе print что-то.Я не знаю, связано ли это с проблемой, описанной здесь.

Заранее благодарен за вашу помощь!

Редактировать: В случае, если это имеет значение: я запускаю python 2.7 в Linux.

1 Ответ

0 голосов
/ 05 марта 2019

Вы вводите большое количество служебной информации, когда используете after для вызова функции каждую миллисекунду, но при каждом вызове вы вытягиваете только один элемент из очереди.То, что вы делаете, - это самое неэффективное из возможных решений

Вместо этого ваша функция должна одновременно вытягивать из очереди более одного элемента.Точное число зависит от многих факторов, и иногда лучший способ узнать это число - провести эксперимент.Вы также можете рассмотреть возможность использования значения больше 1 мс для извлечения элементов из очереди.В 1 мс между вызовами он не дает tkinter много времени для обработки всех других событий, которые ему необходимо обработать.

Например, вы можете after вызывать функцию каждые 10 мс, но вы тянете 100Предметы одновременно.Или вы можете вызывать его каждые 100 мс и полностью истощать очередь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...