Ceci est adapté de ma réponse à un autre poste très similaire.
Construisons un ThreadPool
classe :
class ThreadPool {
public:
void Start();
void QueueJob(const std::function<void()>& job);
void Stop();
void busy();
private:
void ThreadLoop();
bool should_terminate = false; // Tells threads to stop looking for jobs
std::mutex queue_mutex; // Prevents data races to the job queue
std::condition_variable mutex_condition; // Allows threads to wait on new jobs or termination
std::vector<std::thread> threads;
std::queue<std::function<void()>> jobs;
};
ThreadPool::Start
Pour une implémentation efficace du pool de threads, une fois que les threads sont créés conformément à num_threads
il est préférable de ne pas en créer de nouveaux ou en détruire d'anciens (en les rejoignant). Il y aura une pénalité de performance, et cela pourrait même rendre votre application plus lente que la version série. application plus lente que la version série. Ainsi, nous conservons un pool de threads qui peuvent être utilisés à tout moment (s'ils ne sont pas déjà en train d'exécuter une tâche). s'ils ne sont pas déjà en train d'exécuter un travail).
Chaque thread doit exécuter sa propre boucle infinie, en attendant constamment de nouvelles tâches à saisir et à exécuter.
void ThreadPool::Start() {
const uint32_t num_threads = std::thread::hardware_concurrency(); // Max # of threads the system supports
threads.resize(num_threads);
for (uint32_t i = 0; i < num_threads; i++) {
threads.at(i) = std::thread(ThreadLoop);
}
}
ThreadPool::ThreadLoop
La fonction de boucle infinie. Il s'agit d'une while (true)
boucle en attendant que la file d'attente des tâches s'ouvre.
void ThreadPool::ThreadLoop() {
while (true) {
std::function<void()> job;
{
std::unique_lock<std::mutex> lock(queue_mutex);
mutex_condition.wait(lock, [this] {
return !jobs.empty() || should_terminate;
});
if (should_terminate) {
return;
}
job = jobs.front();
jobs.pop();
}
job();
}
}
ThreadPool::QueueJob
Ajoutez un nouveau travail au pool ; utilisez un verrou pour éviter toute course aux données.
void ThreadPool::QueueJob(const std::function<void()>& job) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
jobs.push(job);
}
mutex_condition.notify_one();
}
Pour l'utiliser :
thread_pool->QueueJob([] { /* ... */ });
-
ThreadPool::busy
void ThreadPool::busy() {
bool poolbusy;
{
std::unique_lock<std::mutex> lock(queue_mutex);
poolbusy = jobs.empty();
}
return poolbusy;
}
La fonction busy() peut être utilisée dans une boucle while, de sorte que le thread principal puisse attendre que le threadpool termine toutes les tâches avant d'appeler le destructeur du threadpool.
ThreadPool::Stop
Arrêtez la piscine.
void ThreadPool::Stop() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
should_terminate = true;
}
mutex_condition.notify_all();
for (std::thread& active_thread : threads) {
active_thread.join();
}
threads.clear();
}
Une fois que vous avez intégré ces ingrédients, vous disposez de votre propre pool de threads dynamiques. Ces threads fonctionnent en permanence, attendant travail à faire.
Je m'excuse s'il y a des erreurs de syntaxe, j'ai tapé ce code et j'ai une mauvaise mémoire. Désolé de ne pas pouvoir vous fournir le code complet du pool de threads ; cela violerait l'intégrité de mon travail.
Notes :
- Les blocs de code anonymes sont utilisés de manière à ce que, lorsqu'ils sont quittés, la fonction
std::unique_lock
les variables créées en leur sein sortent de la portée, débloquant le mutex.
-
ThreadPool::Stop
ne mettra pas fin aux travaux en cours, il attendra simplement qu'ils se terminent via la commande active_thread.join()
.