Basj
Basj

Reputation: 46303

Maintain an auto-cleanable list of threads in Python

I maintain a threads list, and I want to auto-remove threads from the list when they are finished.

I found this method:

import threading, time

def f(seconds, info):
    print('starting', seconds)
    time.sleep(seconds)
    print('finished', seconds)
    threads.remove(info['thread'])

def newaction(seconds):
    info = {}
    thread = threading.Thread(target=f, args=(seconds, info))
    info['thread'] = thread
    thread.start()
    threads.append(thread)

threads = []
newaction(1)
newaction(2)
for _ in range(10):
    time.sleep(0.3)
    print(threads)

It works:

starting 1
starting 2
[<Thread(Thread-1, started 1612)>, <Thread(Thread-2, started 712)>]
[<Thread(Thread-1, started 1612)>, <Thread(Thread-2, started 712)>]
[<Thread(Thread-1, started 1612)>, <Thread(Thread-2, started 712)>]
finished 1
[<Thread(Thread-2, started 712)>]
[<Thread(Thread-2, started 712)>]
[<Thread(Thread-2, started 712)>]
finished 2
[]
[]
[]
[]

But the fact of having to pass a dict info is a bit a hack. I used it because obviously I can't pass thread in args...

thread = threading.Thread(target=f, args=(seconds, thread))  
#                                                     ^ not created yet!

...when the Thread object is not created yet!

Is there a more natural way in Python to maintain an auto-cleanable list of threads?

Upvotes: 4

Views: 653

Answers (3)

Evan Messinger
Evan Messinger

Reputation: 198

Subclassing Thread results in a solution with a natural syntax and a secure place to keep the list of threads. You also don't have to include an instruction to remove the thread at the end of every function you want to run in another thread. Simply use the subclass.

import threading, time

class AutoRemovingThread(threading.Thread):
  threads = []
  def __init__(self, func, *args, **kwargs):
    super().__init__()
    self.threads.append(self)
    self.func = func
    self.args = args
    self.kwargs = kwargs
  def run(self):
    self.func(*self.args, **self.kwargs)
    self.threads.remove(self)

def f(seconds):
  print('starting', seconds)
  time.sleep(seconds)
  print('finished', seconds)

def newaction(seconds):
  AutoRemovingThread(f, seconds).start()

newaction(1)
newaction(2)
for _ in range(10):
  time.sleep(0.3)
  print(AutoRemovingThread.threads)

Outputs:

starting 1
starting 2
[<AutoRemovingThread(Thread-1, started 8436)>, <AutoRemovingThread(Thread-2, started 1072)>]
[<AutoRemovingThread(Thread-1, started 8436)>, <AutoRemovingThread(Thread-2, started 1072)>]
[<AutoRemovingThread(Thread-1, started 8436)>, <AutoRemovingThread(Thread-2, started 1072)>]
finished 1
[<AutoRemovingThread(Thread-2, started 1072)>]
[<AutoRemovingThread(Thread-2, started 1072)>]
[<AutoRemovingThread(Thread-2, started 1072)>]
finished 2
[]
[]
[]
[]

python-3.8

Upvotes: 1

igrinis
igrinis

Reputation: 13666

You have the current_thread() function.

import threading, time

def f(seconds):
    print('starting', seconds)
    time.sleep(seconds)
    print('finished', seconds)
    threads.remove(threading.current_thread())

def newaction(seconds):
    thread = threading.Thread(target=f, args=(seconds,))
    thread.start()
    threads.append(thread)

threads = []
newaction(1)
newaction(2)
for _ in range(10):
    time.sleep(0.3)
    print(threads)

Output:

starting 1
starting 2
[<Thread(Thread-1, started 4588)>, <Thread(Thread-2, started 4388)>]
[<Thread(Thread-1, started 4588)>, <Thread(Thread-2, started 4388)>]
[<Thread(Thread-1, started 4588)>, <Thread(Thread-2, started 4388)>]
finished 1
[<Thread(Thread-2, started 4388)>]
[<Thread(Thread-2, started 4388)>]
[<Thread(Thread-2, started 4388)>]
finished 2
[]
[]
[]
[]

Upvotes: 3

Shreyansh
Shreyansh

Reputation: 476

import threading

def get_status_of_threads():
    current_threads = threading.enumerate()

    thread_data = []

    for item in current_threads:
        try:
            print(str(item.target))
        except AttributeError:
            print("item", str(item))
        thread_data.append({"thread_name": item.getName(), "status": int(item.is_alive()), "id": item.ident})
    return thread_data

The above code is tested in Python2.7, you can use this in a separate thread if you want to continuously monitor the threads, or you can expose this as an API, so you can check whenever you want. That will also help in less wastage of resources for the same.

For API, you can use json2html.convert({"thread_data":thread_data}) function to display it in a more aesthetic way in a tabular form.

Upvotes: 0

Related Questions