# Slicer crashes when using QThreadPool to start QRunnable in python module

**URL:** https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596
**Category:** Support
**Created:** [September 27, 2019, 10:17pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596 "2019-09-27T22:17:56Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![Guangshan\_Chen](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/guangshan_chen/32/4832_2.png) [@Guangshan\_Chen](https://discourse.slicer.org/u/Guangshan_Chen)
#### Post date: [September 27, 2019, 10:17pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/1 "2019-09-27T22:17:57Z")

</div>

I Would like to move some jobs to another QThread to avoid the frozen of the main thread (GUI).

I tried python ThreadPoolExecutor to create a thread. However, the child thread is too slow. According to the suggestion of [multithreading-in-extension](https://discourse.slicer.org/t/multithreading-in-extension/6941), I tried QTimer. It also frozen GUI.

Now I am trying QThreadPool by following [multithreading in pyqt](https://www.learnpyqt.com/courses/concurrent-execution/multithreading-pyqt-applications-qthreadpool/) example:

```auto
import vtk, qt, ctk, slicer
from slicer.ScriptedLoadableModule import *

class Worker(qt.QRunnable):
    '''
    Worker thread
    '''

    def __init__ (self):
        super(Worker, self). __init__ ()

    def run(self):
        '''
        Your code goes in this function
        '''
        print("Thread start")
        time.sleep(5)
        print("Thread complete")

class TestWidget(ScriptedLoadableModuleWidget):
    def __init__ (self):
        self.thread_pool = qt.QThreadPool()

    def setup(self):
         ScriptedLoadableModuleWidget.setup(self)
         parametersCollapsibleButton = ctk.ctkCollapsibleButton()
         parametersCollapsibleButton.text = "Parameters"
         self.layout.addWidget(parametersCollapsibleButton)

         parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)

         self.listenButton = qt.QPushButton("Test")
         self.listenButton.toolTip = "Test"
         self.listenButton.enabled = True
         self.listenButton.connect('clicked(bool)', self.onListenButton)
         parametersFormLayout.addRow(self.listenButton)

    def onListenButton(self):
        self.worker = Worker() 
        self.thread_pool.start(self.worker)
      

```

If I click the button to test it, slicer crashes with the following error:  
"  
Received signal 11 SEGV\_MAPERR 559d00000002  
#0 0x7f58faea558f   
#1 0x7f58f98d785d   
#2 0x7f58faea5a9e   
#3 0x7f58e49ff890   
#4 0x7f58eaddfe2d QThreadPoolThread::run()  
#5 0x7f58eade9554 QThreadPrivate::start()  
#6 0x7f58e49f46db start\_thread  
#7 0x7f58d877e88f clone  
"  
I have no idea what is wrong.  
Could anyone point me how to create QThread in Slicer with Python? Thanks.

---

<div class="post-metadata">

### Author: ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)
#### Post date: [September 28, 2019, 7:45pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/2 "2019-09-28T19:45:40Z")

</div>

Python multi-threading is really messy in general and it is further complicated by using a Python interpreter embedded in an application.

I would recommend running background processing in a [Python CLI module](https://github.com/lassoan/SlicerPythonCLIExample) (which runs processing in a separate process) or implement it in a C++ loadable module (where you can use QThread).

---

<div class="post-metadata">

### Author: ![Guangshan\_Chen](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/guangshan_chen/32/4832_2.png) [@Guangshan\_Chen](https://discourse.slicer.org/u/Guangshan_Chen)
#### Post date: [September 30, 2019, 1:44pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/3 "2019-09-30T13:44:47Z")

</div>

Thank you very much!

---

<div class="post-metadata">

### Author: ![Alex\_Vergara](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/alex_vergara/32/15205_2.png) [@Alex\_Vergara](https://discourse.slicer.org/u/Alex_Vergara)
#### Post date: [September 30, 2019, 1:53pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/4 "2019-09-30T13:53:55Z")

</div>

you can do the following:

```python
    def onListenButton(self):
        self.worker = Worker() 
        original_stdin = sys.stdin # Unlock SlicerPython GIL
        sys.stdin = open(os.devnull)
        try:
            self.thread_pool.start(self.worker)
        except Exception as e: # Is something wrong happens, force to terminate the pool
            self.thread_pool.terminate()
        finally:
            self.thread_pool.join()
            sys.stdin.close() # Restores SlicerPython GIL
            sys.stdin = original_stdin

```

This shall work 😉

---

<div class="post-metadata">

### Author: ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)
#### Post date: [September 30, 2019, 1:57pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/5 "2019-09-30T13:57:10Z")

</div>

> [@Alex\_Vergara](#):
>
> original\_stdin = sys.stdin # Unlock SlicerPython GIL

Do you know what are the side effects of this?

---

<div class="post-metadata">

### Author: ![Alex\_Vergara](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/alex_vergara/32/15205_2.png) [@Alex\_Vergara](https://discourse.slicer.org/u/Alex_Vergara)
#### Post date: [September 30, 2019, 1:59pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/6 "2019-09-30T13:59:51Z")

</div>

yep, if you try to do this more than once slicer crashes, but if you wait until the end it works pretty well. So you shall enforce some kind of lock yourself.  
Oh, and this only works with python3, I forgot to mention this.

---

<div class="post-metadata">

### Author: ![Alex\_Vergara](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/alex_vergara/32/15205_2.png) [@Alex\_Vergara](https://discourse.slicer.org/u/Alex_Vergara)
#### Post date: [September 30, 2019, 3:02pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/7 "2019-09-30T15:02:48Z")

</div>

To elaborate more on this: `SlicerPython` is attached to the console which is locked by slicer itself, this procedure liberates temporarily the console input and sets a virtual input with no owner (no GIL). If you try to use slicer console while it is calculating, then `SlycerPython` will not read from it (not that bad). If you try to execute the pool again while is being executed then the system will see colliding threads and will kill slicer.

---

<div class="post-metadata">

### Author: ![Guangshan\_Chen](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/guangshan_chen/32/4832_2.png) [@Guangshan\_Chen](https://discourse.slicer.org/u/Guangshan_Chen)
#### Post date: [September 30, 2019, 3:32pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/8 "2019-09-30T15:32:51Z")

</div>

Thank you very much. I just tried the new code of onListenButton function.  
I got the new errors as the following"

Received signal 11 SEGV\_MAPERR 0000000000a0  
#0 0x7f7f1721558f   
#1 0x7f7f15c4785d   
#2 0x7f7f17215a9e   
#3 0x7f7f00d6f890   
#4 0x7f7f0c259d70 tupledealloc  
#5 0x7f7f0c2473c6 \_PyCFunction\_FastCallDict  
#6 0x7f7f0c1eaa9e \_PyObject\_FastCallDict  
#7 0x7f7f0c21045b PyFile\_WriteObject  
#8 0x7f7f0c21053b PyFile\_WriteString  
#9 0x7f7f0c326e7a PyTraceBack\_Print  
#10 0x7f7f0c31abcf print\_exception\_recursive  
#11 0x7f7f0c31bb48 PyErr\_Display  
#12 0x7f7f0c3237c8 sys\_excepthook  
#13 0x7f7f0c2473ad \_PyCFunction\_FastCallDict  
#14 0x7f7f0c1eaa9e \_PyObject\_FastCallDict  
#15 0x7f7f0c31bcdc PyErr\_PrintEx  
#16 0x7f7f10b19a54 PythonQt::handleError()  
#17 0x7f7f10bc0de1 PythonQtSignalTarget::call()  
#18 0x7f7f10bc0e06 PythonQtSignalTarget::call()  
#19 0x7f7f10bc1625 PythonQtSignalReceiver::qt\_metacall()  
#20 0x7f7f0733c504 QMetaObject::activate()  
#21 0x7f7f0824e1f2 QAbstractButton::clicked()  
#22 0x7f7f0824e3f4 QAbstractButtonPrivate::emitClicked()  
#23 0x7f7f0824ff8e QAbstractButtonPrivate::click()  
#24 0x7f7f082500e5 QAbstractButton::mouseReleaseEvent()  
#25 0x7f7f10e73a05 PythonQtShell\_QPushButton::mouseReleaseEvent()  
#26 0x7f7f081994c8 QWidget::event()  
#27 0x7f7f10e726d7 PythonQtShell\_QPushButton::event()  
#28 0x7f7f0815cdac QApplicationPrivate::notify\_helper()  
#29 0x7f7f08164833 QApplication::notify()  
#30 0x7f7f1eee6536 qSlicerApplication::notify()  
#31 0x7f7f073114e8 QCoreApplication::notifyInternal2()  
#32 0x7f7f0816348f QApplicationPrivate::sendMouseEvent()  
#33 0x7f7f081b301d QWidgetWindow::handleMouseEvent()  
#34 0x7f7f081b5913 QWidgetWindow::event()  
#35 0x7f7f0815cdac QApplicationPrivate::notify\_helper()  
#36 0x7f7f08163e57 QApplication::notify()  
#37 0x7f7f1eee6536 qSlicerApplication::notify()  
#38 0x7f7f073114e8 QCoreApplication::notifyInternal2()  
#39 0x7f7f0793fe37 QGuiApplicationPrivate::processMouseEvent()  
#40 0x7f7f07941d35 QGuiApplicationPrivate::processWindowSystemEvent()  
#41 0x7f7f0791bb7b QWindowSystemInterface::sendWindowSystemEvents()  
#42 0x7f7ee9fceb8b QPAEventDispatcherGlib::processEvents()  
#43 0x7f7f0730fe4a QEventLoop::exec()  
#44 0x7f7f07318850 QCoreApplication::exec()  
#45 0x7f7f1d90d2aa qSlicerCoreApplication::exec()  
#46 0x55bfa73abc87 main  
#47 0x7f7ef49eeb97 \_\_libc\_start\_main

---

<div class="post-metadata">

### Author: ![Alex\_Vergara](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/alex_vergara/32/15205_2.png) [@Alex\_Vergara](https://discourse.slicer.org/u/Alex_Vergara)
#### Post date: [September 30, 2019, 4:08pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/9 "2019-09-30T16:08:33Z")

</div>

instead of using `terminate` and `join`, try using `quit` and `wait` respectively as they are the recommended with QThreadPool.

Anyways, if you want to try the `multiprocessing` module

```python
from multiprocessing import Pool

def worker_wrapper(args):
    worker = Worker(*args)
    return worker.run()

original_stdin = sys.stdin # Unlock SlicerPython GIL
sys.stdin = open(os.devnull)
args = tuple('your arguments here, can be empty so you can omit args')
p = Pool(self.CpuCores)
try: # Start producing results
    iresults = p.imap(worker_wrapper, args) # This is an iterator pointer
    p.close()
    for i, result in enumerate(iresults):
        print("performed {} threads".format(i)) # Here you can follow your threads progress ;)

except Exception as e: # Is something wrong happens, force to terminate the pool
    canceled = True # Necesary, no "return False" allowed here
    p.terminate()
    logging.error(e)

finally:
    p.join()
    sys.stdin.close()
    sys.stdin = original_stdin

```

---

<div class="post-metadata">

### Author: ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)
#### Post date: [September 30, 2019, 4:24pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/10 "2019-09-30T16:24:31Z")

</div>

> [@Alex\_Vergara](#):
>
> To elaborate more on this: `SlicerPython` is attached to the console which is locked by slicer itself, this procedure liberates temporarily the console input and sets a virtual input with no owner (no GIL). If you try to use slicer console while it is calculating, then `SlycerPython` will not read from it (not that bad). If you try to execute the pool again while is being executed then the system will see colliding threads and will kill slicer.

This sounds very fragile. Python CLI or C++ module options are much more reliable. Currently, Slicer’s scheduler runs Python CLIs one by one, but it would be possible to change this so that tasks that do not depend on completion of other tasks could all run in parallel.

---

<div class="post-metadata">

### Author: ![Guangshan\_Chen](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/guangshan_chen/32/4832_2.png) [@Guangshan\_Chen](https://discourse.slicer.org/u/Guangshan_Chen)
#### Post date: [September 30, 2019, 4:24pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/11 "2019-09-30T16:24:40Z")

</div>

Thank you very much! After I try it and get back to you.

---

<div class="post-metadata">

### Author: ![jamesobutler](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/jamesobutler/32/7511_2.png) [@jamesobutler](https://discourse.slicer.org/u/jamesobutler)
#### Post date: [September 30, 2019, 6:51pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/12 "2019-09-30T18:51:48Z")

</div>

> [@lassoan](#):
>
> Currently, Slicer’s scheduler runs Python CLIs one by one, but it would be possible to change this

We previously had a discussion ([Running multiple CLIs at once](https://discourse.slicer.org/t/running-multiple-clis-at-once/7686)) about this, but didn’t take any action yet.

---

<div class="post-metadata">

### Author: ![Guangshan\_Chen](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/guangshan_chen/32/4832_2.png) [@Guangshan\_Chen](https://discourse.slicer.org/u/Guangshan_Chen)
#### Post date: [October 2, 2019, 7:38pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/13 "2019-10-02T19:38:58Z")

</div>

Update:

I have sub-process working with ProcessPoolExecutor.

---

<div class="post-metadata">

### Author: ![Alex\_Vergara](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/alex_vergara/32/15205_2.png) [@Alex\_Vergara](https://discourse.slicer.org/u/Alex_Vergara)
#### Post date: [October 3, 2019, 8:58am UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/14 "2019-10-03T08:58:31Z")

</div>

May you please add your final solution?

---

<div class="post-metadata">

### Author: ![Guangshan\_Chen](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/guangshan_chen/32/4832_2.png) [@Guangshan\_Chen](https://discourse.slicer.org/u/Guangshan_Chen)
#### Post date: [October 3, 2019, 6:31pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/15 "2019-10-03T18:31:50Z")

</div>

Sure.

```auto
import vtk, qt, ctk, slicer
from slicer.ScriptedLoadableModule import *
from concurrent.futures import ProcessPoolExecutor

def task():
   print("sub process start")
   time.sleep(5)
   print("sub process done")

def task_done(future):
    if future.cancelled():
        print("task is cancelled!")
    elif future.done():
        error = future.exception()
        if error:
            print("task error")
        else:
            print(" task is done")

class TestWidget(ScriptedLoadableModuleWidget):
    def __init__ (self):
        self.process_executor = ProcessPoolExecutor(max_workers=3)

    def setup(self):
         ScriptedLoadableModuleWidget.setup(self)
         parametersCollapsibleButton = ctk.ctkCollapsibleButton()
         parametersCollapsibleButton.text = "Parameters"
         self.layout.addWidget(parametersCollapsibleButton)

         parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)

         self.listenButton = qt.QPushButton("Test")
         self.listenButton.toolTip = "Test"
         self.listenButton.enabled = True
         self.listenButton.connect('clicked(bool)', self.onListenButton)
         parametersFormLayout.addRow(self.listenButton)

    def onListenButton(self):
        original_stdin = sys.stdin
        sys.stdin = open(os.devnull)
        try:
            ex = self.process_executor.submit(task)
            ex.add_done_callback(task_done)
        finally:
            sys.stdin.close()
            sys.stdin = original_stdin

```

---

<div class="post-metadata">

### Author: ![Alex\_Vergara](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/alex_vergara/32/15205_2.png) [@Alex\_Vergara](https://discourse.slicer.org/u/Alex_Vergara)
#### Post date: [October 4, 2019, 7:40am UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/16 "2019-10-04T07:40:28Z")

</div>

Remember to add a mutex to prevent duplicated calls to `onListenButton`, if you call it again while it is being executed it will make Slicer to crash.

---

<div class="post-metadata">

### Author: ![Guangshan\_Chen](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/guangshan_chen/32/4832_2.png) [@Guangshan\_Chen](https://discourse.slicer.org/u/Guangshan_Chen)
#### Post date: [October 4, 2019, 3:48pm UTC](https://discourse.slicer.org/t/slicer-crashes-when-using-qthreadpool-to-start-qrunnable-in-python-module/8596/17 "2019-10-04T15:48:42Z")

</div>

> [@Alex\_Vergara](#):
>
> Remember to add a mutex to prevent duplicated calls

Thanks for the suggestion.

I found this poster here talking about using [multiprocessing.Manager](https://stackoverflow.com/questions/35394373/processpoolexecutor-and-lock-in-python). Due to the stdin problem, it does not working in Slicer.  
I simply set a flag before call the subprocess and reset it in callback function.
