0

I want to create threads that will add something to an array, but, if they don't do that in less than 2 seconds, I want to terminate them.

This is a prof of concept, so the code is simple. Every second I want a thread to add that item in the list, so a thread runs after 0, 1, 2, 3 and 4 seconds. The idea is to not let the thread 3 and 4 run.

import threading, time

myList = []

def foo(value):
    global myList
    time.sleep(value)
    print("Value: {}".format(value))
    myList.append(value)

threads = []

for i in range(5):
    th = threading.Thread(target=foo, args=(i,))
    threads.append(th)

for th in threads:
    th.start()

What do I do now? I tried using some other logic like using

th.join(timeout)

But that doesn't seem to work.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • First: use a `collections.deque` or a `queue.Queue` to add items to, they are thread-safe (while lists are not). But to the actual problem, what if you just pass the start time to `foo`, and then simply check the time delta to the start time before you add them to the queue? e.g. inside `foo` just check `if time.now() - start_time < 2: q.append(value)` – alkasm Nov 27 '20 at 22:14
  • You can't "kill" a thread. You must communicate or send a signal somehow to it that it's time to `return` (or they can do it based on some internal reason, like a certain amount of time passing). – martineau Nov 27 '20 at 22:24
  • Is the problem that you cannot terminate a thread, or that you can't measure the time it takes to execute the function that you want to take less than 2s to run? For the first you can do something like @martineau said, such as causing an exception or making the function return something. For the other, you could use `asyncio` in each thread, having it run two functions asyncronously, the main function that you want to executre, and the other to check the time passed. – Countour-Integral Nov 27 '20 at 22:48

2 Answers2

1

As I said in a comment you can't really "kill" a thread (externally). However they can "commit suicide" by returning or raising a exception.

Below is example of doing the latter when the thread's execution time has exceeded a given amount of time. Note that this is not the same as doing a join(timeout) call, which only blocks until the thread ends or the specified amount of time has elapsed. That's why the printing of value and its appending to the list happens regardless of whether the thread finishes before the call to join() times-out or not.

I got the basic idea of using sys.settrace() from the tutorial titled Different ways to kill a Thread — although my implementation is slightly different. Also note that this approach likely introduces a significant amount of overhead.

import sys
import threading
import time


class TimelimitedThread(threading.Thread):
    def __init__(self, *args, time_limit, **kwargs):
        self.time_limit = time_limit
        self._run_backup = self.run  # Save superclass run() method.
        self.run = self._run  # Change it to custom version.
        super().__init__(*args, **kwargs)

    def _run(self):
        self.start_time = time.time()
        sys.settrace(self.globaltrace)
        self._run_backup()  # Call superclass run().
        self.run = self._run_backup  # Restore original.

    def globaltrace(self, frame, event, arg):
        return self.localtrace if event == 'call' else None

    def localtrace(self, frame, event, arg):
        if(event == 'line' and
           time.time()-self.start_time > self.time_limit):  # Over time?
            raise SystemExit()  # Terminate thread.
        return self.localtrace


THREAD_TIME_LIMIT = 2.1  # Secs
threads = []
my_list = []

def foo(value):
    global my_list
    time.sleep(value)
    print("Value: {}".format(value))
    my_list.append(value)

for i in range(5):
    th = TimelimitedThread(target=foo, args=(i,), time_limit=THREAD_TIME_LIMIT)
    threads.append(th)

for th in threads:
    th.start()

for th in threads:
    th.join()

print('\nResults:')
print('my_list:', my_list)

Output:

Value: 0
Value: 1
Value: 2

Results:
my_list: [0, 1, 2]
martineau
  • 119,623
  • 25
  • 170
  • 301
-2

Join() is used to wait for the respective thread to finish. To terminate a thread, use stop().. You can try as follows: time.sleep(N) th.join()