diff --git a/src/polystar/frame_generators/camera_frame_generator.py b/src/polystar/frame_generators/camera_frame_generator.py
index c2649fc2ded68134c4f5e11f9b39ed23d301b964..5543fc2786d67728b469b8b6fa541b51add0fd3e 100644
--- a/src/polystar/frame_generators/camera_frame_generator.py
+++ b/src/polystar/frame_generators/camera_frame_generator.py
@@ -1,10 +1,10 @@
-from threading import Thread
 from typing import Any, Iterator
 
 import cv2
 
 from polystar.frame_generators.cv2_frame_generator_abc import CV2FrameGenerator
 from polystar.models.image import Image
+from polystar.utils.thread import MyThread
 
 
 class CameraFrameGenerator(CV2FrameGenerator):
@@ -17,25 +17,14 @@ class CameraFrameGenerator(CV2FrameGenerator):
         while True:
             yield self.camera_thread.current_frame.copy()
 
-    def __del__(self):
-        self.camera_thread.stop()
 
-
-class CameraThread(Thread):
+class CameraThread(MyThread):
     def __init__(self, it: Iterator[Image]):
         super().__init__()
         self.it = it
-        self.running = True
-        self._get_next_frame()
-
-    def run(self):
-        while self.running:
-            self._get_next_frame()
-
-    def stop(self):
-        self.running = False
+        self.step()
 
-    def _get_next_frame(self):
+    def step(self):
         try:
             self.current_frame = next(self.it)
         except StopIteration:
diff --git a/src/polystar/utils/thread.py b/src/polystar/utils/thread.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed556d64ef9ed10fe059af8c76645ad1cadafd80
--- /dev/null
+++ b/src/polystar/utils/thread.py
@@ -0,0 +1,27 @@
+from threading import Thread
+from typing import List
+
+
+class MyThread(Thread):
+    THREADS: List["MyThread"] = []
+
+    def __init__(self):
+        super().__init__()
+        self.running = False
+        self.THREADS.append(self)
+
+    def run(self) -> None:
+        self.running = True
+        while self.running:
+            self.step()
+
+    def step(self):
+        pass
+
+    def stop(self):
+        self.running = False
+
+    @staticmethod
+    def stop_all():
+        for thread in MyThread.THREADS:
+            thread.stop()
diff --git a/src/research/scripts/demo_pipeline_camera.py b/src/research/scripts/demo_pipeline_camera.py
index a06d889db19977059c5b63ecc335befbcfe93e36..00c6d6cd782768cca0bd70731d3ca5c3e320905e 100644
--- a/src/research/scripts/demo_pipeline_camera.py
+++ b/src/research/scripts/demo_pipeline_camera.py
@@ -8,6 +8,7 @@ from polystar.models.image import Image
 from polystar.target_pipeline.debug_pipeline import DebugTargetPipeline
 from polystar.target_pipeline.target_pipeline import NoTargetFoundException
 from polystar.utils.fps import FPS
+from polystar.utils.thread import MyThread
 from polystar.view.cv2_results_viewer import CV2ResultViewer
 
 
@@ -49,3 +50,4 @@ class CameraPipelineDemo:
 
 if __name__ == "__main__":
     make_injector().get(CameraPipelineDemo).run()
+    MyThread().stop_all()