Repository: Project-MONAI/MONAI

Issue:
test_smartcachedataset thread hangs occasionally
**Describe the bug**
```
[2022-12-05T19:30:09.040Z] 
Loading dataset:   0%|          | 0/2 [00:00<?, ?it/s]
Loading dataset: 100%|██████████| 2/2 [00:00<00:00, 7577.79it/s]
[2022-12-05T19:30:09.040Z] .Finished test: test_datalist (tests.test_smartcachedataset.TestSmartCacheDataset) (0.00285s)
[2022-12-05T19:30:09.040Z] Starting test: test_set_data (tests.test_smartcachedataset.TestSmartCacheDataset)...
[2022-12-05T19:30:09.040Z] 
Loading dataset:   0%|          | 0/5 [00:00<?, ?it/s]
Loading dataset: 100%|██████████| 5/5 [00:00<00:00, 4430.91it/s]
[2022-12-05T21:52:01.667Z] Sending interrupt signal to process
[2022-12-05T21:52:01.669Z] Killing processes
[2022-12-05T21:52:02.262Z] kill finished with exit code 0
[2022-12-05T21:52:02.264Z] Sending interrupt signal to process
[2022-12-05T21:52:02.264Z] Killing processes
[2022-12-05T21:52:02.596Z] kill finished with exit code 2
[2022-12-05T21:52:02.824Z] Terminated
[2022-12-05T21:52:02.827Z] script returned exit code 143
```


Discussion:

Resolving patch:
diff --git a/monai/data/dataset.py b/monai/data/dataset.py
--- a/monai/data/dataset.py
+++ b/monai/data/dataset.py
@@ -1091,16 +1091,14 @@ def is_started(self):
         Check whether the replacement thread is already started.
 
         """
-        if self._replace_mgr is None:
-            return False
-        return self._replace_mgr.is_alive()
+        return False if self._replace_mgr is None else self._replace_mgr.is_alive()
 
     def start(self):
         """
         Start the background thread to replace training items for every epoch.
 
         """
-        if self._replace_mgr is None or not self.is_started():
+        if not self.is_started():
             self._restart()
 
     def _restart(self):
@@ -1141,8 +1139,7 @@ def update_cache(self):
         If the cache has been shutdown before, need to restart the `_replace_mgr` thread.
 
         """
-        if not self._replace_mgr.is_alive():
-            self._restart()
+        self.start()
 
         # make sure update is done
         while not self._try_update_cache():
@@ -1173,7 +1170,7 @@ def shutdown(self):
         # wait until replace mgr is done the current round
         while not self._try_shutdown():
             time.sleep(0.01)
-        self._replace_mgr.join()
+        self._replace_mgr.join(300)
 
     def _replace_cache_thread(self, index: int):
         """
diff --git a/monai/data/video_dataset.py b/monai/data/video_dataset.py
--- a/monai/data/video_dataset.py
+++ b/monai/data/video_dataset.py
@@ -20,14 +20,21 @@
 from monai.utils.enums import ColorOrder
 from monai.utils.module import optional_import
 
+__all__ = ["VideoDataset", "VideoFileDataset", "CameraDataset"]
+
 if TYPE_CHECKING:
     import cv2
 
     has_cv2 = True
 else:
-    cv2, has_cv2 = optional_import("cv2")
+    cv2, has_cv2 = None, None
 
-__all__ = ["VideoDataset", "VideoFileDataset", "CameraDataset"]
+
+def import_cv():
+    """Import cv2. Put it inside a function to avoid webcam lights blinking on ``import monai``."""
+    global cv2
+    global has_cv2
+    cv2, has_cv2 = optional_import("cv2")
 
 
 class SuppressStderr:
@@ -50,6 +57,9 @@ def __exit__(self, *_):
 
 
 class VideoDataset:
+    # import inside class to avoid webcam blinking on ``import monai``.
+    import_cv()
+
     def __init__(
         self,
         video_source: Union[str, int],


Test patch: