Search code examples
pythonthreadpoolexecutor

return of ThreadPoolExecutor to array of tasks


Below is an example code I got from https://tutorialedge.net/python/concurrency/python-threadpoolexecutor-tutorial/

from concurrent.futures import ThreadPoolExecutor
import threading
import random

taskarr = []

def task(n,c):
    print("Executing our Task = {} and {}".format(n,c))
    print("Task Executed {}".format(threading.current_thread()))

def main():
    executor = ThreadPoolExecutor(max_workers=3)
    task1 = executor.submit(task(1,'A'))
    task2 = executor.submit(task(2,'B'))

if __name__ == '__main__':
    main()

in this, if executor.submit is returned to simple variable task1, it works. But my requirement is to return it to an array. so when i do, taskarr[0] = executor.submit(task(1,'A')) it throws error as,

TypeError: 'function' object does not support item assignment

Solution

  • I could not reproduce your TypeError; apparently you have some more code that you did not show us. As rocksportrocker pointed out in their comment, you can't assign values to an index that does not yet exist, as in your attempt to assign the future returned from executor.submit() to index 0 of the empty list typearr. This will raise the mentioned IndexError.

    However, I could produce some TypeError and that is due to the fact that you call submit incorrectly.

    The Executor documentation defines the parameter fn to be a callable object, e.g. a function. Now, when you call submit like you do:

    executor.submit(task(1,'A'))
    

    task(1,'A') is executed in your main thread and its return value (None in your case) is then passed to the pool thread. The pool thread then tries to execute None(1, 'A') and raises a TypeError: 'NoneType' object is not callable, which you don't see, because it is wrapped in the future. You would see it if you called task1.result().

    That means, that it is your main thread that executes your task instead of your pool threads, as should have become visible from your second print statement in task:

    Task Executed <_MainThread(MainThread, started 11064)>
    

    In order to have your pool threads do the work, you need to pass the function object task to executor.submit, like so:

    taskarr = []
    
    def task(n,c):
        print("Executing our Task = {} and {}".format(n,c))
        time.sleep(3)  # added this to make sure that two different threads from the pool get a task
        print("Task Executed {}".format(threading.current_thread()))
    
    def main():
        executor = ThreadPoolExecutor(max_workers=3)
        taskarr.append(executor.submit(task, 1,'A'))
        taskarr.append(executor.submit(task, 2,'B'))
        # to show that the main thread continues to execute
        # while the pool threads work on the tasks
        print("{}: tasks submitted".format(threading.current_thread().name))
    
    if __name__ == '__main__':
        main()
    

    Output:

    Executing our Task = 1 and A
    Executing our Task = 2 and B
    MainThread: tasks submitted
    Task Executed <Thread(ThreadPoolExecutor-0_0, started daemon 12192)>
    Task Executed <Thread(ThreadPoolExecutor-0_1, started daemon 11800)>