Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python multithreading send a function to run in main thread from subthread and wait for it until finished

I got a question about multi-threading in python. Basically in my software, I got one function which is expensive to run. Therefore, I put it to run in subthread. However, during the function running, it will call a function and change some values of a object, which will trigger GUI changes. Therefore, I need to put it back to mainthread.

The problem is I need to wait for this function is completely finished in main thread, then to proceed the function in subthread.

Therefore, I need a clever way to send job back to main thread and wait it finished. Can some one please give me any advise how to do it?

Thanks very much. I add my running thread method at below

Best Regards,

import Queue
import logging
import sys
import time
import threading


DISABLE_THREADING = False

queue = Queue.Queue()


def run_in_sub_thread_named(name, function, *params):

    if DISABLE_THREADING:
        return function(*params)
    th = _DEThread(function, *params)
    task_info = TaskInfo(name)
    th.task_info = task_info
    running_tasks[th] = task_info
    th.start()
    return th

def run_in_main_thread(function, *params, **kwargs):
    cur_thread = threading.currentThread()
    cur_thread_name = cur_thread.getName()
    in_main_thread = (cur_thread_name == 'MainThread')
    if in_main_thread:
        function(*params, **kwargs)
    else:
        queue.put((function, params, kwargs))



class _DEThread:
    def __init__(self, function, *params):
        self.thread = threading.Thread(target=self.execute)
        self.thread.setDaemon(True)
        self.function = function
        self.params = params
        self.name = ''

    def start(self):
        self.thread.start()

    def execute(self):
        try:
            if self.params is ():
                res = self.function()
            else:
                res = self.function(*self.params)
        except:
            run_in_main_thread(self._update_task_info,
                'Exception: %s' % repr(sys.exc_info()))
            raise
        run_in_main_thread(self._update_task_info, res)

    def _update_task_info(self, res):
        if self in running_tasks:
            task_info = running_tasks[self]
            task_info.end_time = time.time()
            task_info.result = res
            finished_tasks[self] = task_info
            del running_tasks[self]
like image 450
Feng Zhu Avatar asked Apr 01 '16 09:04

Feng Zhu


1 Answers

Typically, the way to call functions between threads in Qt is with Signals and Slots. You can also use QMetaObject.invokeMethod to call a function in another thread, and use the Qt.BlockingQueuedConnection type to force it to block the thread until the main thread function completes. You're going to need to use QThreads instead of python threads though. Take a look at the Worker pattern described in the QThread docs to see how to refactor your code to work with QThreads

def function_in_thread(self):
    # This will block the thread until function completes
    # in main thread.
    QtCore.QMetaObject.invokeMethod(self.main_gui, 'nameOfMethod', QtCore.Qt.BlockingQueuedConnection)
like image 168
Brendan Abel Avatar answered Oct 13 '22 01:10

Brendan Abel