# Using Python multithreading in 3D Slicer

**URL:** <https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299>\
**Category:** Development\
**Tags:** python\
**Created:** [October 18, 2023, 2:56pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299 "2023-10-18T14:56:46Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Hunger-beat](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/hunger-beat/32/67898_2.png) [@Hunger-beat](https://discourse.slicer.org/u/Hunger-beat)\
**Post date:** [October 18, 2023, 2:56pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/1 "2023-10-18T14:56:47Z")

</div>

Dear forum，

I am using Python to write an extension module in 3D Slicer. I need sub threads to perform heavy calculations while also interacting with the UI for some data. I used Python’s threading for multithreading programming, but there were some issues.

When I clicked the push button, the child thread started, but only ran a little before stopping. The child thread can only continue working when I slide the mouse over the image window. This makes me very confused. How can I solve this problem?

```auto
    self.ui.pushButton.clicked.connect(self.testButton)
    def testButton(self):
        self.ui.pushButton.setEnabled(False)
        self.thread1 = threading.Thread(target=self.execute)
        self.thread1.start()

    def execute(self):
        count = 10000000
        while count > 0: # Simulate time-consuming operations
            if count % 100000 == 0:
                print(count)
            count = count-1
        print("Finished")
        self.ui.pushButton.setEnabled(True)

```

I have also read some posts and learned that CLI module or QProcess may be a better solution, but currently I still want to use multithreading to solve it.

Thanks for your help!

---

<div class="post-metadata">

**Author:** ![cpinter](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/cpinter/32/7995_2.png) [@cpinter](https://discourse.slicer.org/u/cpinter)\
**Post date:** [October 18, 2023, 2:59pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/2 "2023-10-18T14:59:58Z")

</div>

If you use threading in Python with `threading`, those will also run on the main thread, so it will not be executed on a different core. At least as far as I know.

I suggest you take a look at [GitHub - pieper/SlicerParallelProcessing: Slicer modules for running subprocesses to operate on data in parallel](https://github.com/pieper/SlicerParallelProcessing)  
We successfully use it in a project so although @pieper says it is still experimental, I can confirm that it works.

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [October 18, 2023, 5:57pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/3 "2023-10-18T17:57:36Z")

</div>

Yes, multithreading python is complicated by the [Global Interpreter Lock (GIL)](https://en.wikipedia.org/wiki/Global_interpreter_lock). As @cpinter mentioned, the SlicerParallelProcessing extension is set up so you can transfer data with an instance of Slicer’s python environment running in a different process space. You can access all the python packages you have installed in you PythonSlicer environment, but you cannot access the GUI or MRML data, only the data you explicitly pass to the subprocess.

---

<div class="post-metadata">

**Author:** ![Hunger-beat](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/hunger-beat/32/67898_2.png) [@Hunger-beat](https://discourse.slicer.org/u/Hunger-beat)\
**Post date:** [October 19, 2023, 3:09am UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/4 "2023-10-19T03:09:44Z")

</div>

@pieper @cpinter Sorry for the late reply. Thank you both for your suggestions！ I will start learning parallel processing. However, I still don’t understand this sentence enough. If I want to access GUI or MRML data in subprocess, what should I do?

> [@pieper](#):
>
> but you cannot access the GUI or MRML data, only the data you explicitly pass to the subprocess.

Thank you again for your response and contribution to the community.

---

<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:** [October 19, 2023, 3:17am UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/5 "2023-10-19T03:17:19Z")

</div>

> [@Hunger-beat](#):
>
> When I clicked the push button, the child thread started, but only ran a little before stopping. The child thread can only continue working when I slide the mouse over the image window. This makes me very confused.

When you move the mouse over the image window then you yield the GIL, this way all Python threads get a chance to run for a little. You can automate this regular yielding as it is done in the [SimpleFilters module](https://github.com/SimpleITK/SlicerSimpleFilters/blob/master/SimpleFilters/SimpleFilters.py), by calling Python `sleep()` function regularly using a `QTimer`.

This technique allows keeping the GUI responsive while some computation is running in the background, but may not improve performance. See some more information about this [here](https://pythonspeed.com/articles/python-gil/).

If you want to improve performance (not just GUI responsiveness):

- If the code in the background thread does not release the GIL (e.g., pure Python code that operates on Python objects) then execution will not be faster by introducing more threads. You can run the processing code in a different process, for example using ParallelProcessing Slicer extension.
- If the code in the background thread releases the GIL for significant time periods then you get true parallelism: your code may use multiple CPU cores. For example, _SimpleITK_ releases the GIL, so if you use SimpleITK filters in your background thread then you will get not just responsive GUI but also processing code running in parallel with Python code. _VTK_ does not release the GIL in Slicer (it would require setting a hidden `VTK_PYTHON_FULL_THREADSAFE` CMake flag to do so - see [here](https://stackoverflow.com/questions/65928669/python-vtk-and-gil-release)).

@pieper @cpinter @jcfr If we want to improve Python threading support in Slicer, then we could consider:

- yielding the Python GIL using a timer (so that Python threads just work, without each developer having to set up this mechanism)
- build VTK with `VTK_PYTHON_FULL_THREADSAFE=ON` (to allow VTK to run in background Python threads without blocking Python code execution on other threads)

---

<div class="post-metadata">

**Author:** ![Hunger-beat](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/hunger-beat/32/67898_2.png) [@Hunger-beat](https://discourse.slicer.org/u/Hunger-beat)\
**Post date:** [October 19, 2023, 3:40am UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/6 "2023-10-19T03:40:02Z")

</div>

Thank you very much for your reply! I think using QTimer may be useful, and I will give it a try later. In addition, I will also try to learn ParallelProcessing to see which method is more suitable for me. Because I have just come into contact with Slicer, I am not very familiar with these. Thank you again for your help!

---

<div class="post-metadata">

**Author:** ![cpinter](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/cpinter/32/7995_2.png) [@cpinter](https://discourse.slicer.org/u/cpinter)\
**Post date:** [October 19, 2023, 8:29am UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/7 "2023-10-19T08:29:33Z")

</div>

> [@lassoan](#):
>
> VTK\_PYTHON\_FULL\_THREADSAFE=ON

Are you aware of any drawback related to enabling this flag? It seems a quick and clean way to improve multi-threading if it does not cause other issues. At least much of the Python processing code I have written has VTK calls all over the place, so it seems to be quite relevant.

---

<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:** [October 19, 2023, 12:10pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/8 "2023-10-19T12:10:08Z")

</div>

The GIL is a mutex, so there is an extra cost in each VTK Python call because of its unlocking and locking. In most cases it is probably insignificant, as Python is very slow anyway. But we should stress test this a bit to make sure we don’t introduce any perceivable slowdown in normal single-threaded operation. We could ask ParaView/VTK developers about this, too, as they have more experience with this.

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [October 19, 2023, 2:02pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/9 "2023-10-19T14:02:55Z")

</div>

@Hunger-beat a lot depends on what you are doing in the threads. This is a longstanding issue. As you can [see on this page](https://www.na-mic.org/wiki/2013_Project_Week:Threaded_SimpleITK_Modules) SimpleITK GIL support was added in 2013, using methods discussed on the VTK mailing list in 2005. Using a thread and releasing the GIL when calling long-running C or C++ code makes sense since it can run independent of Python. But it doesn’t make sense if the code is executing Python or interacting with the GUI, which are single threaded.

> [@lassoan](#):
>
> build VTK with `VTK_PYTHON_FULL_THREADSAFE=ON`

I’ve never tried that, but it might be okay. I agree we’d need to check if it introduces subtle bugs or performance issues.

---

<div class="post-metadata">

**Author:** ![Hunger-beat](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/hunger-beat/32/67898_2.png) [@Hunger-beat](https://discourse.slicer.org/u/Hunger-beat)\
**Post date:** [October 20, 2023, 5:39am UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/10 "2023-10-20T05:39:28Z")

</div>

Okay, I understand. I will study carefully. Thank you again for your answers!

---

<div class="post-metadata">

**Author:** ![zina](https://avatars.discourse-cdn.com/v4/letter/z/f08c70/32.png) [@zina](https://discourse.slicer.org/u/zina)\
**Post date:** [July 15, 2024, 1:43pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/11 "2024-07-15T13:43:49Z")

</div>

Did you implement improve “Python threading support in Slicer” already?

---

<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:** [July 15, 2024, 3:00pm UTC](https://discourse.slicer.org/t/using-python-multithreading-in-3d-slicer/32299/12 "2024-07-15T15:00:56Z")

</div>

There has not been much interest in this since this discussion last year (perhaps because threading in Python does not actually mean parallel execution, so not really impactful), so we did not make any changes. You can do the same what is done in SimpleFilters module (calling Python `sleep()` function regularly using a `QTimer` - see details above) and let us know if you find it useful.
