Thread pool
| Пул потоков | |
|---|---|
| Thread pool | |
| Схема принципа работы пула потоков | |
| Тип | Параллельного программирования |
| Назначение | Эффективное управление многопоточностью |
| Плюсы | Снижение накладных расходов на создание и уничтожение потоков |
| Минусы | При неоптимально великом или малом размере теряет эффективность |
| Родственные шаблоны | Пул объектов, Производитель-потребитель |
| Описан в Design Patterns | Нет |
Thread pool (с англ. — «Пул потоков») — паттерн проектирования программного обеспечения, предназначенный для эффективного управления потоками исполнения при параллельной обработке задач. Вместо создания и удаления нового потока под каждую задачу, паттерн предлагает создание набора потоков, обрабатывающих потокобезопасную очередь[1].
Основы
Широкое распространение получили многопоточные процессорные архитектуры. Они позволяют обрабатывать множество задач одновременно, что значительно повышает эффективность обработки клиентских запросов. Но каждое создание и уничтожение потока чревато накладными расходами на системные вызовы[1]. В архитектуре многопоточных приложений, в которой на каждую задачу создаётся новый поток при большом количестве задач добавляются расходы на выделение памяти под собственный стек и переключение контекста.
Пул потоков снижает эти накладные расходы, заметно увеличивая производительность в ряде сценариев. Вместо создания потока под каждую задачу, пул создаётся единовременно, и таким образом расходы на создание потоков сокращаются с количества задач до количества потоков. В частности пул потоков сокращает расходы во время исполнения под высокой нагрузкой[2].
Размер
Высокое значение для производительности и эффективности пула потоков имеет его размер. Неоптимально большой размер пула потоков приводит к простою части потоков и неоправданным расходам на их содержание. В то же время слишком малый размер пула нивелирует его преимущества вследствие необходимости создавать новые потоки во время работы[1].
Исследования показали, что количество потоков в пуле, определённое в прямой пропорциональной зависимости от количества ядер центрального процессора при игнорировании прочих показателей, является неоптимальным с точки зрения производительности[1]. При определении оптимального размера пула следует учитывать в том числе и текущую нагрузку на сервер[1].
Примеры
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#define THREADS 4
#define QUEUE_SIZE 8
#define TASKS 8
typedef struct {
void (*function)(const char *);
const char *arg;
} Task;
typedef struct {
pthread_mutex_t lock;
pthread_cond_t tasknotify;
pthread_cond_t donenotify;
pthread_t *threads;
Task *queue;
Task *taskptr;
bool shutdown;
} ThreadPool;
void
worker(ThreadPool *pool)
{
Task task;
for (;;) {
pthread_mutex_lock(&pool->lock);
while (pool->taskptr == NULL) {
if (!pool->shutdown) {
pthread_cond_wait(&pool->tasknotify, &pool->lock);
} else {
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL);
}
}
task = *pool->taskptr;
if (pool->taskptr == pool->queue)
pool->taskptr = NULL;
else
pool->taskptr--;
pthread_mutex_unlock(&pool->lock);
task.function(task.arg);
pthread_cond_signal(&pool->donenotify);
}
}
bool
taskadd(ThreadPool *pool, void (*function)(const char *), const char *id)
{
pthread_mutex_lock(&pool->lock);
if (pool->taskptr == pool->queue + QUEUE_SIZE - 1 || pool->shutdown) {
pthread_mutex_unlock(&pool->lock);
return false;
}
if (pool->taskptr == NULL)
pool->taskptr = pool->queue;
else
pool->taskptr++;
pool->taskptr->function = function;
pool->taskptr->arg = id;
pthread_cond_signal(&pool->tasknotify);
pthread_mutex_unlock(&pool->lock);
return true;
}
void
taskwork(const char *id)
{
printf("Задача «%s» выполняется потоком %ld\n", id, (long)pthread_self());
}
int
main(void)
{
Task queue[QUEUE_SIZE];
pthread_t threads[THREADS];
ThreadPool pool = {
.threads = threads,
.queue = queue,
.taskptr = NULL,
.shutdown = false
};
pthread_mutex_init(&pool.lock, NULL);
pthread_cond_init(&pool.tasknotify, NULL);
pthread_cond_init(&pool.donenotify, NULL);
for (int i = 0; i < THREADS; i++) {
pthread_create(&pool.threads[i], NULL, (void*)worker, &pool);
}
const char *taskid[TASKS] = { "Один", "Два", "Три", "Четыре", "Пять", "Шесть", "Семь", "Восемь" };
for (int i = 0; i < TASKS; i++)
taskadd(&pool, taskwork, taskid[i]);
pthread_mutex_lock(&pool.lock);
while (pool.taskptr != NULL)
pthread_cond_wait(&pool.donenotify, &pool.lock);
pool.shutdown = true;
pthread_cond_broadcast(&pool.tasknotify);
pthread_mutex_unlock(&pool.lock);
for (int i = 0; i < THREADS; i++)
pthread_join(pool.threads[i], NULL);
pthread_mutex_destroy(&pool.lock);
pthread_cond_destroy(&pool.tasknotify);
pthread_cond_destroy(&pool.donenotify);
puts("Пул потоков остановлен");
return 0;
}
Примечания
- ↑ 1 2 3 4 5 Yibei Ling, Tracy Mullen, Xiaola Lin. Analysis of Optimal Thread Pool Size (англ.) // ACM SIGOPS : журнал. — 2000. — 14 February. — doi:10.1145/346152.346320.
- ↑ Liangzhou Wang, Chaobin Wang. Producer-consumer Model Based Thread Pool Design (англ.) // IOP Publishing : журнал. — 2020. — doi:10.1088/1742-6596/1616/1/012073.
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.