If you don't mind executing other's code, here's mine:
Note: There is lot of extra code you may want to remove [added for better clarificaiton and demonstration how it works]
Note: Python naming conventions were used for method names and variable names instead of camelCase.
Working procedure:
- MultiThread class will initiate with no of instances of threads by sharing lock, work queue, exit flag and results.
- SingleThread will be started by MultiThread once it creates all instances.
- We can add works using MultiThread (It will take care of locking).
- SingleThreads will process work queue using a lock in middle.
- Once your work is done, you can destroy all threads with shared boolean value.
- Here, work can be anything. It can automatically import (uncomment import line) and process module using given arguments.
- Results will be added to results and we can get using get_results
Code:
import threading import queue class SingleThread(threading.Thread): def __init__(self, name, work_queue, lock, exit_flag, results): threading.Thread.__init__(self) self.name = name self.work_queue = work_queue self.lock = lock self.exit_flag = exit_flag self.results = results def run(self): # print("Coming %s with parameters %s", self.name, self.exit_flag) while not self.exit_flag: # print(self.exit_flag) self.lock.acquire() if not self.work_queue.empty(): work = self.work_queue.get() module, operation, args, kwargs = work.module, work.operation, work.args, work.kwargs self.lock.release() print("Processing : " + operation + " with parameters " + str(args) + " and " + str(kwargs) + " by " + self.name + "\n") # module = __import__(module_name) result = str(getattr(module, operation)(*args, **kwargs)) print("Result : " + result + " for operation " + operation + " and input " + str(args) + " " + str(kwargs)) self.results.append(result) else: self.lock.release() # process_work_queue(self.work_queue) class MultiThread: def __init__(self, no_of_threads): self.exit_flag = bool_instance() self.queue_lock = threading.Lock() self.threads = [] self.work_queue = queue.Queue() self.results = [] for index in range(0, no_of_threads): thread = SingleThread("Thread" + str(index+1), self.work_queue, self.queue_lock, self.exit_flag, self.results) thread.start() self.threads.append(thread) def add_work(self, work): self.queue_lock.acquire() self.work_queue._put(work) self.queue_lock.release() def destroy(self): self.exit_flag.value = True for thread in self.threads: thread.join() def get_results(self): return self.results class Work: def __init__(self, module, operation, args, kwargs={}): self.module = module self.operation = operation self.args = args self.kwargs = kwargs class SimpleOperations: def sum(self, *args): return sum([int(arg) for arg in args]) @staticmethod def mul(a, b, c=0): return int(a) * int(b) + int(c) class bool_instance: def __init__(self, value=False): self.value = value def __setattr__(self, key, value): if key != "value": raise AttributeError("Only value can be set!") if not isinstance(value, bool): raise AttributeError("Only True/False can be set!") self.__dict__[key] = value # super.__setattr__(key, bool(value)) def __bool__(self): return self.value if __name__ == "__main__": multi_thread = MultiThread(5) multi_thread.add_work(Work(SimpleOperations(), "mul", [2, 3], {"c":4})) while True: data_input = input() if data_input == "": pass elif data_input == "break": break else: work = data_input.split() multi_thread.add_work(Work(SimpleOperations(), work[0], work[1:], {})) multi_thread.destroy() print(multi_thread.get_results())
from multiprocessing.pool import ThreadPool.I know about the GIL. However, in my usecase, the function will be an IO-bound C function for which the python wrapper will release the GIL before the actual function call.?