Mantid
Loading...
Searching...
No Matches
ThreadPool.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
11
15#include "MantidKernel/Task.h"
17
18#include <Poco/Thread.h>
19
20#include <algorithm>
21#include <sstream>
22#include <stdexcept>
23// needed on windows and any place missing openmp
24#if defined(_WIN32) || !defined(_OPENMP)
25#include <Poco/Environment.h>
26#endif
27
28namespace Mantid::Kernel {
29
30//--------------------------------------------------------------------------------
43ThreadPool::ThreadPool(ThreadScheduler *scheduler, size_t numThreads, ProgressBase *prog)
44 : m_scheduler(std::unique_ptr<ThreadScheduler>(scheduler)), m_started(false),
45 m_prog(std::unique_ptr<ProgressBase>(prog)) {
46 if (!m_scheduler)
47 throw std::invalid_argument("NULL ThreadScheduler passed to ThreadPool constructor.");
48
49 if (numThreads == 0) {
50 // Uses Poco to find how many cores there are.
52 } else
53 m_numThreads = numThreads;
54 // std::cout << m_numThreads << " m_numThreads \n";
55}
56
57//--------------------------------------------------------------------------------
60ThreadPool::~ThreadPool() = default;
61
62//--------------------------------------------------------------------------------
68// windows hangs with openmp for some reason
69#if defined(_WIN32) || !defined(_OPENMP)
70 int physicalCores = Poco::Environment::processorCount();
71#else
72 int physicalCores = PARALLEL_GET_MAX_THREADS;
73#endif
74
75 // MultiThreaded.MaxCores limits the number of cores when it is above 0; otherwise all of them are used
76 const auto maxCores = Kernel::ConfigService::Instance().getValue<int>("MultiThreaded.MaxCores");
77 if (maxCores.value_or(0) > 0)
78 return static_cast<size_t>(std::min(maxCores.value(), physicalCores));
79 return static_cast<size_t>(physicalCores);
80}
81
82//--------------------------------------------------------------------------------
93void ThreadPool::start(double waitSec) {
94 if (m_started)
95 throw std::runtime_error("Threads have already started.");
96 // Now, launch that many threads and let them wait for new tasks.
97 m_threads.clear();
98 m_runnables.clear();
99 for (size_t i = 0; i < m_numThreads; i++) {
100 // Make a descriptive name
101 std::ostringstream name;
102 name << "Thread" << i;
103 // Create the thread
104 auto thread = std::make_unique<Poco::Thread>(name.str());
105 // Make the runnable object and run it
106 auto runnable = std::make_unique<ThreadPoolRunnable>(i, m_scheduler.get(), m_prog.get(), waitSec);
107 thread->start(*runnable);
108 m_threads.emplace_back(std::move(thread));
109 m_runnables.emplace_back(std::move(runnable));
110 }
111 // Yep, all the threads are running.
112 m_started = true;
113}
114
115//--------------------------------------------------------------------------------
122void ThreadPool::schedule(const std::shared_ptr<Task> &task, bool start) {
123 if (task) {
124 m_scheduler->push(task);
125 // Start all the threads if they were not already.
126 if (start && !m_started)
127 this->start();
128 }
129}
130
131//--------------------------------------------------------------------------------
139bool compareTasks(Task *lhs, Task *rhs) { return (lhs->cost() > rhs->cost()); }
140
141//--------------------------------------------------------------------------------
150 // Are the threads REALLY started, or did they exit due to lack of tasks?
151 if (m_started) {
152 m_started = false;
153 // If any of the threads are running, then YES, it is really started.
154 for (auto &thread : m_threads)
155 m_started = m_started || thread->isRunning();
156 }
157
158 // Start all the threads if they were not already.
159 if (!m_started)
160 this->start();
161
162 // Clear any wait times so that the threads stop waiting for new tasks.
163 for (auto &runnable : m_runnables)
164 runnable->clearWait();
165
166 // Sequentially join all the threads.
167 for (auto &thread : m_threads) {
168 thread->join();
169 }
170
171 // Clear the vectors (the threads are deleted now).
172 m_threads.clear();
173
174 // Get rid of the runnables too
175 m_runnables.clear();
176
177 // This will make threads restart
178 m_started = false;
179
180 // Did one of the threads abort or throw an exception?
181 if (m_scheduler->getAborted()) {
182 // Re-raise the error
183 throw m_scheduler->getAbortException();
184 }
185}
186
187} // namespace Mantid::Kernel
std::string name
Definition Run.cpp:60
const std::vector< double > & rhs
#define PARALLEL_GET_MAX_THREADS
ThreadScheduler * m_scheduler
A Task is a unit of work to be scheduled and run by a ThreadPool.
Definition Task.h:29
virtual double cost()
What is the computational cost of this task?
Definition Task.h:53
void schedule(const std::shared_ptr< Task > &task, bool start=false)
Schedule a task for later execution.
std::vector< std::unique_ptr< Poco::Thread > > m_threads
Vector with all the threads that are started.
Definition ThreadPool.h:59
ThreadPool(ThreadScheduler *scheduler=new ThreadSchedulerFIFO(), size_t numThreads=0, ProgressBase *prog=nullptr)
Constructor.
size_t m_numThreads
Number of cores used.
Definition ThreadPool.h:53
bool m_started
Have the threads started?
Definition ThreadPool.h:65
void start(double waitSec=0.0)
Start the threads and begin looking for tasks.
std::vector< std::unique_ptr< ThreadPoolRunnable > > m_runnables
Vector of the POCO-required classes to actually run in a thread.
Definition ThreadPool.h:62
std::unique_ptr< ProgressBase > m_prog
Progress reporter.
Definition ThreadPool.h:68
void joinAll()
Wait for all threads that have started to finish.
static size_t getNumPhysicalCores()
Return the number of physical cores available on the system, limited by MultiThreaded....
std::unique_ptr< ThreadScheduler > m_scheduler
The ThreadScheduler instance taking care of task scheduling.
Definition ThreadPool.h:56
The ThreadScheduler object defines how tasks are allocated to threads and in what order.
bool compareTasks(Task *lhs, Task *rhs)
Method to perform sorting of task lists.
STL namespace.