Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 142fad4

Browse files
committed
Use WeakSets rather than manual pruning to prevent unbounded growth of dead thread references.
1 parent 833d912 commit 142fad4

2 files changed

Lines changed: 8 additions & 40 deletions

File tree

Lib/concurrent/futures/process.py

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,28 +66,14 @@
6666
# workers to exit when their work queues are empty and then waits until the
6767
# threads/processes finish.
6868

69-
_thread_references = set()
69+
_live_threads = weakref.WeakSet()
7070
_shutdown = False
7171

7272
def _python_exit():
7373
global _shutdown
7474
_shutdown = True
75-
for thread_reference in _thread_references:
76-
thread = thread_reference()
77-
if thread is not None:
78-
thread.join()
79-
80-
def _remove_dead_thread_references():
81-
"""Remove inactive threads from _thread_references.
82-
83-
Should be called periodically to prevent memory leaks in scenarios such as:
84-
>>> while True:
85-
>>> ... t = ThreadPoolExecutor(max_workers=5)
86-
>>> ... t.map(int, ['1', '2', '3', '4', '5'])
87-
"""
88-
for thread_reference in set(_thread_references):
89-
if thread_reference() is None:
90-
_thread_references.discard(thread_reference)
75+
for thread in _live_threads:
76+
thread.join()
9177

9278
# Controls how many more calls than processes will be queued in the call queue.
9379
# A smaller number will mean that processes spend more time idle waiting for
@@ -279,7 +265,6 @@ def __init__(self, max_workers=None):
279265
worker processes will be created as the machine has processors.
280266
"""
281267
_check_system_limits()
282-
_remove_dead_thread_references()
283268

284269
if max_workers is None:
285270
self._max_workers = multiprocessing.cpu_count()
@@ -316,7 +301,7 @@ def _start_queue_management_thread(self):
316301
self._shutdown_process_event))
317302
self._queue_management_thread.daemon = True
318303
self._queue_management_thread.start()
319-
_thread_references.add(weakref.ref(self._queue_management_thread))
304+
_live_threads.add(self._queue_management_thread)
320305

321306
def _adjust_process_count(self):
322307
for _ in range(len(self._processes), self._max_workers):

Lib/concurrent/futures/thread.py

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,14 @@
2525
# workers to exit when their work queues are empty and then waits until the
2626
# threads finish.
2727

28-
_thread_references = set()
28+
_live_threads = weakref.WeakSet()
2929
_shutdown = False
3030

3131
def _python_exit():
3232
global _shutdown
3333
_shutdown = True
34-
for thread_reference in _thread_references:
35-
thread = thread_reference()
36-
if thread is not None:
37-
thread.join()
38-
39-
def _remove_dead_thread_references():
40-
"""Remove inactive threads from _thread_references.
41-
42-
Should be called periodically to prevent memory leaks in scenarios such as:
43-
>>> while True:
44-
... t = ThreadPoolExecutor(max_workers=5)
45-
... t.map(int, ['1', '2', '3', '4', '5'])
46-
"""
47-
for thread_reference in set(_thread_references):
48-
if thread_reference() is None:
49-
_thread_references.discard(thread_reference)
50-
34+
for thread in _live_threads:
35+
thread.join()
5136
atexit.register(_python_exit)
5237

5338
class _WorkItem(object):
@@ -95,8 +80,6 @@ def __init__(self, max_workers):
9580
max_workers: The maximum number of threads that can be used to
9681
execute the given calls.
9782
"""
98-
_remove_dead_thread_references()
99-
10083
self._max_workers = max_workers
10184
self._work_queue = queue.Queue()
10285
self._threads = set()
@@ -125,7 +108,7 @@ def _adjust_thread_count(self):
125108
t.daemon = True
126109
t.start()
127110
self._threads.add(t)
128-
_thread_references.add(weakref.ref(t))
111+
_live_threads.add(t)
129112

130113
def shutdown(self, wait=True):
131114
with self._shutdown_lock:

0 commit comments

Comments
 (0)