Гарантируется ли, что ThreadPoolExecutor равномерно распределит N задач по N потокам?

#python #multithreading

Вопрос:

Если мне нужно выполнить задачу 5 раз, и обычно это занимает 10-30 секунд на каждую задачу, гарантирует ли это, что я сразу начну все 5 в отдельных потоках?

 with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(task, [0, 1, 2, 3, 4]))
 

Если ответ отрицательный, то как бы я получил желаемую гарантию?

Комментарии:

1. @user207421 так и есть. Просто не хватает места, чтобы полностью указать… Моя заметка просто спасает отвечающего от написания эссе.

2. @user207421 так и есть. Это более конкретно. Это не значит, что название не отражает мой реальный вопрос. Не могли бы вы предложить внести правку?

3. Я уже предлагал вам заставить их согласиться. Я также предлагаю, чтобы ответ на вопрос в вашем названии был «нет, если это явно не указано», но соответствие вашего названия вашему вопросу зависит от вас. Это в ваших же интересах-привлечь нужных читателей. Я не зарабатываю на этом ни цента.

Ответ №1:

Ничто в жизни не гарантировано, но можно с уверенностью утверждать, что каждая задача будет выполняться в отдельных потоках в пуле потоков.

Пять отправленных задач попадают во входную очередь задач, которые необходимо выполнить. Если бы рабочая функция task выполнялась очень быстро, теоретически было бы возможно , чтобы один из потоков в пуле потоков извлек из очереди входных задач все задачи и запустил их одну за другой до того, как у любого из остальных 4 потоков появилась возможность запустить. Но, учитывая, что у нас есть относительно длительная задача, всем 5 потокам будет предоставлен временной отрезок, прежде чем какой-либо один поток сможет завершить выполнение хотя бы одной задачи.

Вот пример чрезвычайно сложной задачи ввода-вывода:

 from concurrent.futures import ThreadPoolExecutor

def task(x):
    import threading
    import time

    time.sleep(2)
    print(threading.get_ident(), x)

with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(task, [0, 1, 2, 3, 4]))
 

С принтами:

 15492 0
21120 1
3160 2
12548 3
13468 4
 

И вот пример чрезвычайно сложной задачи, связанной с процессором:

 from concurrent.futures import ThreadPoolExecutor

ONE_SECOND_ITERATIONS = 20_000_000

def one_second():
    sum = 0
    for _ in range(ONE_SECOND_ITERATIONS):
        sum  = 1
    return sum

def task(x):
    import threading

    for _ in range(2):
        one_second()
    print(threading.get_ident(), x)

with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(task, [0, 1, 2, 3, 4]))
 

С принтами:

 14660 0
21776 4
3384 3
16596 1
4924 2
 

Вот случай, когда task выполняется чрезвычайно быстро, и один поток выполняет все 5 задач:

 from concurrent.futures import ThreadPoolExecutor

def task(x):
    import threading

    return (threading.get_ident(), x)

with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(task, [0, 1, 2, 3, 4]))
    print(results)
 

С принтами:

 [(18104, 0), (18104, 1), (18104, 2), (18104, 3), (18104, 4)]
 

Комментарии:

1. Отлично! Спасибо за все примеры.