pythonmultithreadingconcurrent.futures

python 3 - concurrent.futures - get thread number without adding a function


Currently this code prints,

MainThread done at sec 1
MainThread done at sec 1
MainThread done at sec 2
MainThread done at sec 1
MainThread done at sec 0
MainThread done at sec 3
MainThread done at sec 2
MainThread done at sec 5
MainThread done at sec 4

I need it to print

MainThread 1 done at sec 1
MainThread 3 done at sec 1
MainThread 2 done at sec 2
MainThread 3 done at sec 1
MainThread 1 done at sec 0
MainThread 2 done at sec 3
MainThread 2 done at sec 2
MainThread 3 done at sec 5
MainThread 1 done at sec 4

How can I do this, by modifying this 1 line of code.

print(threading.current_thread().name + ' done at sec ' + str(future.result()))

Here is the full code

import concurrent.futures
import threading
import pdb
import time
import random


def Threads2(time_sleep):
    time.sleep(time_sleep)
    return time_sleep


all_sections = [random.randint(0, 5) for iii in range(10)]

test1 = []
with concurrent.futures.ThreadPoolExecutor(max_workers=3, thread_name_prefix="Ok") as executor:
    working_threads = {executor.submit(Threads2, time_sleep): time_sleep for index, time_sleep in enumerate(all_sections)}

    for future in concurrent.futures.as_completed(working_threads):
        print(threading.current_thread().name + ' done at sec ' + str(future.result()))

Solution

  • future doesn't know which thread ran the function, you need to store that yourself.

    you can wrap the functor in a wrapper that will return whatever you want from the executing thread.

    import concurrent.futures
    import threading
    import pdb
    import time
    import random
    from functools import wraps
    
    def wrap_function(func):
        @wraps(func)
        def wrapper_functor(*args, **kwargs):
            return (threading.current_thread().name, 
                    threading.current_thread().ident, 
                    func(*args, **kwargs)) # calls the wrapped function
        return wrapper_functor  # return a functor that wraps the function
    
    def Threads2(time_sleep):
        time.sleep(time_sleep)
        return time_sleep
    
    all_sections = [random.randint(0, 5) for iii in range(10)]
    
    test1 = []
    with concurrent.futures.ThreadPoolExecutor(
            max_workers=3, thread_name_prefix="Ok") as executor:
        working_threads = {
            executor.submit(wrap_function(Threads2), time_sleep): time_sleep for
            index, time_sleep in enumerate(all_sections)}
    
        for future in concurrent.futures.as_completed(working_threads):
            thread_name, thread_id, result = future.result()
            print(thread_name, thread_id, 'done at sec', str(result))
    
    Ok_0 18532 done at sec 0
    Ok_2 12424 done at sec 1
    Ok_0 18532 done at sec 3
    Ok_2 12424 done at sec 2
    Ok_1 19356 done at sec 4
    Ok_0 18532  done at sec 4
    Ok_1 19356  done at sec 3
    Ok_2 12424  done at sec 4
    Ok_0 18532  done at sec 3
    Ok_1 19356  done at sec 4
    

    if you want just 0,1,2 then you can use thread_name.split('_')[-1]


    a super compressed form to do this with a lambda (which shouldn't pass a code review) is

    working_threads = {
        executor.submit(lambda *args, **kwargs: (
            threading.current_thread().name,
            threading.current_thread().ident,
            Threads2(*args, **kwargs)), time_sleep): time_sleep for
        index, time_sleep in enumerate(all_sections)}