Run the worker function(s) concurrently in multiple threads. If `worker_func` is a single callable, it is used for all threads. If it is a list of callables, each callable is used for one thread.
(worker_func, nthreads=None, args=(), kwargs={})
| 251 | |
| 252 | |
| 253 | def run_concurrently(worker_func, nthreads=None, args=(), kwargs={}): |
| 254 | """ |
| 255 | Run the worker function(s) concurrently in multiple threads. |
| 256 | |
| 257 | If `worker_func` is a single callable, it is used for all threads. |
| 258 | If it is a list of callables, each callable is used for one thread. |
| 259 | """ |
| 260 | from collections.abc import Iterable |
| 261 | |
| 262 | if nthreads is None: |
| 263 | nthreads = len(worker_func) |
| 264 | if not isinstance(worker_func, Iterable): |
| 265 | worker_func = [worker_func] * nthreads |
| 266 | assert len(worker_func) == nthreads |
| 267 | |
| 268 | barrier = threading.Barrier(nthreads) |
| 269 | |
| 270 | def wrapper_func(func, *args, **kwargs): |
| 271 | # Wait for all threads to reach this point before proceeding. |
| 272 | barrier.wait() |
| 273 | func(*args, **kwargs) |
| 274 | |
| 275 | with catch_threading_exception() as cm: |
| 276 | workers = [ |
| 277 | threading.Thread(target=wrapper_func, args=(func, *args), kwargs=kwargs) |
| 278 | for func in worker_func |
| 279 | ] |
| 280 | with start_threads(workers): |
| 281 | pass |
| 282 | |
| 283 | # If a worker thread raises an exception, re-raise it. |
| 284 | if cm.exc_value is not None: |
| 285 | raise cm.exc_value |
searching dependent graphs…