Как вернуть значение из функции, запущенной QThread и Queue

пожалуйста, объясните, как мы отправляем / получаем данные из потока, управляемого очередью....

первый подкласс "QThread", определяющий его run() метод, который запускается при QThread ' s.start() называется:

class SimpleThread(QtCore.QThread):
    def __init__(self, queue, parent=None):
        QtCore.QThread.__init__(self, parent)      
        self.queue=queue        
    def run(self):
        while True:
            arg=self.queue.get() 
            self.fun(arg)    
            self.queue.task_done()
    def fun(self, arg):
        for i in range (3):
            print 'fun: %s'%i
            self.sleep(1)
        return arg+1

затем я объявляю два экземпляра потока (так что берутся только два ядра процессора) отправка self.queue экземпляр в качестве аргумента.

self.queue=queue.Queue()
for i in range(2):
    thread=SimpleThread(self.queue)
    thread.start()

теперь, если я правильно понимаю thread.start() ничего не начинается. Настоящий "старт" происходит только тогда, когда я звоню. queue.put():

for arg in [1,2,3]: self.queue.put(arg)

последняя строка-это то, что делает "настоящий" вызов. Помимо создания и запуска в очередь put() позволяет сохранить любое произвольное значение для каждого в очередь. .put() делает сразу несколько вещей: создает, запускает, перемещает обработку по очереди и позволяет разместить переменную "внутри" элемента очереди (которую позже можно получить изнутри функции-процессора: используя элемент очереди '.получить`)( метод.)

но как мне вернуть значение из

1 ответов


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

class SimpleThread(QtCore.QThread):
    def __init__(self, queue, result_queue, parent=None):
        QtCore.QThread.__init__(self, parent)      
        self.queue=queue
        self.result_queue = result_queue

    def run(self):
        while True:
            arg=self.queue.get() 
            self.fun(arg)    
            self.queue.task_done()

    def fun(self, arg):
        for i in range (3):
            print 'fun: %s'%i
            self.sleep(1)
        self.result_queue.put(arg+1)

def handle_results(result_queue):
   while True:
       result = result_queue.get()
       print("Got result {}".format(result))

основные темы:

self.queue=queue.Queue()
self.result_queue = queue.Queue()

result_handler = threading.Thread(target=handle_results, self.result_queue)
for i in range(2):
    thread=SimpleThread(self.queue, self.result_queue)
    thread.start()

делать это таким образом будет держать вас от блокировки цикла событий GUI в то время как вы ждете результатов. Вот как будет выглядеть эквивалент с multiprocessing.pool.ThreadPool:

from multiprocessing.pool import ThreadPool
import time


def fun(arg):
    for i in range (3):
        print 'fun: %s'%i
        time.sleep(1)
    return arg+1

def handle_result(result):
   print("got result {}".format(result))

pool = ThreadPool(2)
pool.map_async(fun, [1,2,3], callback=handle_result)

что намного проще. Он внутренне создает поток обработки результатов, который будет автоматический вызов handle_result при fun завершается.

тем не менее, вы используете QThread, и вы хотите, чтобы результаты обновляли виджеты GUI, поэтому вы действительно хотите, чтобы ваши результаты были отправлены обратно в основной поток, а не в поток обработки результатов. В этом случае имеет смысл использовать сигнальную систему Qt, чтобы вы могли безопасно обновлять GUI при получении результата:

from PyQt4 import QtCore, QtGui
import sys
import Queue as queue

class ResultObj(QtCore.QObject):
    def __init__(self, val):
        self.val = val

class SimpleThread(QtCore.QThread):
    finished = QtCore.pyqtSignal(object)

    def __init__(self, queue, callback, parent=None):
        QtCore.QThread.__init__(self, parent)      
        self.queue = queue
        self.finished.connect(callback)

    def run(self):
        while True:
            arg = self.queue.get() 
            if arg is None: # None means exit
                print("Shutting down")
                return
            self.fun(arg)    

    def fun(self, arg):
        for i in range(3):
            print 'fun: %s' % i
            self.sleep(1)
        self.finished.emit(ResultObj(arg+1))


class AppWindow(QtGui.QMainWindow):
    def __init__(self):
        super(AppWindow, self).__init__()
        mainWidget = QtGui.QWidget()
        self.setCentralWidget(mainWidget)
        mainLayout = QtGui.QVBoxLayout()
        mainWidget.setLayout(mainLayout)  
        button = QtGui.QPushButton('Process')
        button.clicked.connect(self.process)
        mainLayout.addWidget(button)

    def handle_result(self, result):
        val = result.val
        print("got val {}".format(val))
        # You can update the UI from here.

    def process(self):
        MAX_CORES=2
        self.queue = queue.Queue()
        self.threads = []
        for i in range(MAX_CORES):
            thread = SimpleThread(self.queue, self.handle_result)
            self.threads.append(thread)
            thread.start()  

        for arg in [1,2,3]:
            self.queue.put(arg)

        for _ in range(MAX_CORES): # Tell the workers to shut down
            self.queue.put(None)

app = QtGui.QApplication([])
window = AppWindow()
window.show()
sys.exit(app.exec_())

выход при нажатии кнопки:

fun: 0
 fun: 0
fun: 1
 fun: 1
fun: 2
 fun: 2
fun: 0
got val 2
got val 3
Shutting down
fun: 1
fun: 2
Shutting down
got val 4