Максимальное использование рабочего потока

Чтобы решить проблему (и лучше понять многозадачность), я написал реализацию небольшого пула потоков. Этот пул потоков запускает несколько рабочих потоков, которые извлекают задачи из очереди по мере их добавления клиентом пула потоков. Для целей этого вопроса, когда очередь задач пуста, все рабочие потоки завершаются.

Проведя базовый бенчмаркинг, я обнаружил, что приложение тратит около 60% своего времени на ожидание получения блокировки очереди. Предположительно, это в основном происходит в рабочих потоках.

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

EDIT: вот примерный псевдокод, который должен несколько проиллюстрировать ситуацию. Это единственные два места, где блокировка устанавливается/снимается во время выполнения рабочих потоков (что составляет большую часть времени работы приложения).

std::list<task_t> task_list;

// Called by the client to add tasks to the thread pool
void insert_task(const task_t& task)
{
    lock_type listlock(task_mutex);

    task_list.push_back(task);
}

// The base routine of each thread in the pool. Some details
// such as lifetime management have been omitted for clarity.
void worker_thread_base()
{
    while (true)
    {
        task_t task;

        {
        lock_type listlock(task_mutex);

        if (task_list.empty())
            continue;

        task = task_list.front();

        task_list.pop_front();
        }

        do_task(task);
    }
}

person fbrereto    schedule 05.08.2010    source источник
comment
Нам нужен код, чтобы понять, почему ваше приложение застревает в блокировке очереди. Я думаю, что ваши потоки должны застрять в состоянии ожидания (используя условную переменную или что-то подобное) большую часть времени.   -  person Starkey    schedule 05.08.2010


Ответы (2)


Ваш проект построен таким образом, что каждый поток сидит и "крутится", пытаясь получить блокировку. Это будет происходить постоянно, если каждый рабочий поток не выполняет работу — в этом случае блокировка не будет получена, и работа будет выполняться.

Поскольку все ваши потоки просто сидят, вращаясь на блокировке, вы будете использовать довольно много процессорного времени на ожидание. Это несколько ожидаемо, учитывая ваш дизайн.

Вы обнаружите, что процент заблокированного времени, вероятно, резко сократится, если у вас будет меньше рабочих потоков — и в момент, когда у вас больше рабочих элементов, чем потоков, вы потратите очень мало времени на ожидание этой блокировки.

Гораздо лучше было бы использовать некую форму очереди без блокировки для вашей рабочей очереди, так как это могло бы предотвратить ожидание на этом этапе. Кроме того, наличие дескриптора ожидания, который может блокировать рабочие потоки до тех пор, пока в очереди не появится работа, предотвратит ненужное вращение.

person Reed Copsey    schedule 05.08.2010

Вы пытаетесь сделать это с помощью одного замка, нескольких замков? Мьютексы? Какую семантику ожидания вы используете?

Я бы предположил из вашего описания (и это чисто предположение), что у вас есть что-то похожее на:

lock(theLock) {
 // ... do lots of work ...
}

В вашем основном потоке, который содержит код для отправки в легкие потоки. Одна из причин, по которой вы можете увидеть увеличенное время ожидания, заключается в том, что вам нужно получать сигналы от запущенных потоков о том, что они поставлены в очередь и ожидают выполнения (опять же, это предположение, поскольку вы не дали никакого кода).

Один из способов решить эту проблему — переключиться с использования явной блокировки, как указано выше, на использование сигнального мьютекса, который активируется, когда вы хотите, чтобы один из потоков перехватил работу.

Однако, не видя вашей текущей реализации, я не уверен, что смогу сделать больше.

person GrayWizardx    schedule 05.08.2010