Created
September 16, 2016 13:33
-
-
Save brandonto/f15630bee4f53976a6bad9238c9fb093 to your computer and use it in GitHub Desktop.
Basic ThreadPool Implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <condition_variable> | |
| #include <iostream> | |
| #include <memory> | |
| #include <mutex> | |
| #include <queue> | |
| #include <thread> | |
| #include <vector> | |
| #include <cstdlib> | |
| #include "Timer.h" | |
| class ThreadPool | |
| { | |
| public: | |
| ThreadPool(int numThreads) : | |
| _stop(false) | |
| { | |
| const int NUM_THREADS = numThreads; | |
| // Create worker threads | |
| std::cout << "ThreadPool::ThreadPool(): Creating worker threads." << std::endl; | |
| for (int i=0; i<NUM_THREADS; i++) | |
| { | |
| _workerThreads.push_back(std::thread( | |
| [this, i](void) | |
| { | |
| //std::function<void()> task; | |
| while (true) | |
| { | |
| std::unique_lock<std::mutex> lock(this->_task_mutex); | |
| // Wake up if notified and either class destructor was | |
| // called or task queue is not empty | |
| this->_task_cv.wait(lock, | |
| [this](void) | |
| { | |
| return this->_stop || !this->_task_queue.empty(); | |
| }); | |
| std::cout << "Thread " << i << " is woken up." << std::endl; | |
| // Class destructor was called, join thread | |
| if (this->_stop) | |
| { | |
| std::cout << "Thread " << i << " is stopping." << std::endl; | |
| return; | |
| } | |
| // Dequeue and perform task | |
| auto task = this->_task_queue.front(); | |
| this->_task_queue.pop(); | |
| lock.unlock(); | |
| std::cout << "Thread " << i << " is performing task." << std::endl; | |
| task(); | |
| } | |
| } | |
| )); | |
| } | |
| std::cout << "ThreadPool::ThreadPool(): Finished creating worker threads." << std::endl; | |
| } | |
| ~ThreadPool() | |
| { | |
| std::unique_lock<std::mutex> lock(_task_mutex); | |
| // Wake up all worker threads and signal them to stop | |
| std::cout << "ThreadPool::~ThreadPool(): Sending stop thread signals." << std::endl; | |
| _stop = true; | |
| _task_cv.notify_all(); | |
| lock.unlock(); | |
| // Join all worker threads | |
| std::cout << "ThreadPool::~ThreadPool(): Joining worker threads." << std::endl; | |
| for (auto &thread : _workerThreads) | |
| { | |
| thread.join(); | |
| } | |
| std::cout << "ThreadPool::~ThreadPool(): Finished joining threads." << std::endl; | |
| } | |
| void enqueueTask(std::function<void()> task) | |
| { | |
| // Add task to queue | |
| std::unique_lock<std::mutex> lock(_task_mutex); | |
| _task_queue.push(task); | |
| lock.unlock(); | |
| // Wake up a worker thread | |
| _task_cv.notify_one(); | |
| } | |
| private: | |
| std::vector<std::thread> _workerThreads; | |
| std::queue<std::function<void()>> _task_queue; | |
| std::mutex _task_mutex; | |
| std::condition_variable _task_cv; | |
| bool _stop; | |
| }; | |
| // | |
| // Main program | |
| // | |
| int main(int argc, char *argv[]) | |
| { | |
| Timer timer; | |
| ThreadPool threadPool(2); | |
| threadPool.enqueueTask( | |
| [](void) | |
| { | |
| Timer timer; | |
| timer.start(); | |
| while (timer.getTimeOnTimer() < 2000); | |
| timer.stop(); | |
| std::cout << "Task complete." << std::endl; | |
| }); | |
| threadPool.enqueueTask( | |
| [](void) | |
| { | |
| std::cout << "Task complete." << std::endl; | |
| }); | |
| threadPool.enqueueTask( | |
| [](void) | |
| { | |
| Timer timer; | |
| timer.start(); | |
| while (timer.getTimeOnTimer() < 1000); | |
| timer.stop(); | |
| std::cout << "Task complete." << std::endl; | |
| }); | |
| timer.start(); | |
| while (timer.getTimeOnTimer() < 4000); | |
| timer.stop(); | |
| return 0; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //****************************************************************************** | |
| // | |
| // Timer.cpp | |
| // | |
| // Copyright (c) 2016 Brandon To | |
| // This code snippet is licensed under the BSD 3-clause license. | |
| // | |
| // Author: Brandon To | |
| // Created: August 24, 2016 | |
| // | |
| //****************************************************************************** | |
| #include "Timer.h" | |
| Timer::Timer() : | |
| _startTime(steady_clock::time_point::min()), | |
| _tempTime(steady_clock::time_point::min()), | |
| _pausedTime(steady_clock::time_point::min()), | |
| _started(false), | |
| _paused(false) | |
| { | |
| } | |
| void | |
| Timer::start() | |
| { | |
| if (_started) | |
| { | |
| return; | |
| } | |
| _started = true; | |
| _paused = false; | |
| _startTime = steady_clock::now(); | |
| _tempTime = _startTime; | |
| _pausedTime = steady_clock::time_point::min(); | |
| } | |
| void | |
| Timer::stop() | |
| { | |
| if (!_started) | |
| { | |
| return; | |
| } | |
| _started = false; | |
| _paused = false; | |
| _startTime = steady_clock::time_point::min(); | |
| _tempTime = steady_clock::time_point::min(); | |
| _pausedTime = steady_clock::time_point::min(); | |
| } | |
| void | |
| Timer::pause() | |
| { | |
| if (!_started || _paused) | |
| { | |
| return; | |
| } | |
| _paused = true; | |
| _pausedTime = steady_clock::now(); | |
| } | |
| void | |
| Timer::resume() | |
| { | |
| if (!_started || !_paused) | |
| { | |
| return; | |
| } | |
| _paused = false; | |
| _tempTime = _tempTime + (steady_clock::now() - _pausedTime); | |
| _pausedTime = steady_clock::time_point::min(); | |
| } | |
| uint32_t | |
| Timer::getTimeOnTimer() | |
| { | |
| if (!_started) | |
| { | |
| return 0; | |
| } | |
| if (_paused) | |
| { | |
| auto durationOnTimer = _pausedTime - _tempTime; | |
| auto ms = duration_cast<milliseconds>(durationOnTimer); | |
| return ms.count(); | |
| } | |
| auto durationOnTimer = steady_clock::now() - _tempTime; | |
| auto ms = duration_cast<milliseconds>(durationOnTimer); | |
| return ms.count(); | |
| } | |
| uint32_t | |
| Timer::getTimeSinceTimerStarted() | |
| { | |
| if (!_started) | |
| { | |
| return 0; | |
| } | |
| auto durationSinceTimerStarted = steady_clock::now() - _startTime; | |
| auto ms = duration_cast<milliseconds>(durationSinceTimerStarted); | |
| return ms.count(); | |
| } | |
| bool | |
| Timer::isStarted() | |
| { | |
| return _started; | |
| } | |
| bool | |
| Timer::isPaused() | |
| { | |
| return _paused; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //****************************************************************************** | |
| // | |
| // Timer.h | |
| // | |
| // Copyright (c) 2016 Brandon To | |
| // This code snippet is licensed under the BSD 3-clause license. | |
| // | |
| // Author: Brandon To | |
| // Created: August 24, 2016 | |
| // | |
| //****************************************************************************** | |
| #ifndef __TIMER_H__ | |
| #define __TIMER_H__ | |
| #include <chrono> | |
| #include <cstdint> | |
| using namespace std::chrono; | |
| class Timer | |
| { | |
| public: | |
| Timer(); | |
| void start(); | |
| void stop(); | |
| void pause(); | |
| void resume(); | |
| uint32_t getTimeOnTimer(); | |
| uint32_t getTimeSinceTimerStarted(); | |
| bool isStarted(); | |
| bool isPaused(); | |
| private: | |
| steady_clock::time_point _startTime; | |
| steady_clock::time_point _tempTime; | |
| steady_clock::time_point _pausedTime; | |
| bool _started; | |
| bool _paused; | |
| }; | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment