MDA
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups
ThreadPool.hpp
1 /*
2  * ThreadPool2.hpp
3  *
4  * Created on: Nov 8, 2013
5  * Author: ckeme_01
6  */
7 
8 // based on https://github.com/progschj/ThreadPool/blob/master/ThreadPool.h
9 
10 #ifndef THREADPOOL2_HPP_
11 #define THREADPOOL2_HPP_
12 
13 
14 #include <atomic>
15 #include <vector>
16 #include <queue>
17 #include <memory>
18 #include <thread>
19 #include <mutex>
20 #include <condition_variable>
21 #include <future>
22 #include <functional>
23 #include <stdexcept>
24 //#include <chrono>
25 
26 
27 template<typename T>
28 class ThreadPool {
29 
30 private:
31  // need to keep track of threads so we can join them
32  std::vector< std::thread > workers;
33  std::atomic<int> _running;
34  // the task queue
35  std::queue<std::function<void(T&)> > _tasks;
36 
37  // synchronization
38  std::mutex queue_mutex;
39  std::condition_variable condition;
40 
41  std::mutex _returned_mutex;
42  std::condition_variable _returned;
43  bool _stop;
44  void _join();
45 
46 public:
47  ThreadPool(size_t, const T &local_data);
48  ThreadPool(const ThreadPool&&) = delete;
49 
50  template<class F, class... Args>
51  void addTask(F&& f, Args&&... args);
52  ~ThreadPool();
53 
54  void stop();
55  void wait();
56  int running()
57  {
58  return _running.load();
59  }
60 
61 };
62 
63 // the constructor just launches some amount of workers
64 template<typename T>
65 inline ThreadPool<T>::ThreadPool(size_t threads, const T &local_data)
66  : workers(), _running(0), _tasks(), queue_mutex(), condition(), _returned_mutex(), _returned(), _stop(false)
67 {
68  for(size_t i = 0;i<threads;++i)
69  {
70  workers.emplace_back(
71  [this] (const T &local_data)
72  {
73  std::function<void(T&)> task;
74  T data=local_data;
75  while(true)
76  {
77  {
78  std::unique_lock<std::mutex> lock(this->queue_mutex);
79  while(!this->_stop && _tasks.empty())
80  this->condition.wait(lock);
81  if(this->_stop && _tasks.empty())
82  return;
83  task = _tasks.front();
84  _tasks.pop();
85  }
86  task(data);
87  {
88  std::unique_lock<std::mutex> lock(_returned_mutex);
89  --_running;
90  }
91  _returned.notify_all();
92  }
93  },
94  local_data
95  );
96  }
97 }
98 
99 // add new work item to the pool
100 template<typename T>
101 template<class F, class... Args>
102 void ThreadPool<T>::addTask(F&& f, Args&&... args)
103 {
104  // don't allow enqueueing after stopping the pool
105  if(_stop)
106  throw std::runtime_error("enqueue on stopped ThreadPool");
107  {
108  std::unique_lock<std::mutex> lock(_returned_mutex);
109  while(_running.load()>workers.size()*1.5)
110  this->_returned.wait(lock);
111  }
112  auto task = std::function<void(T&)> (
113  std::bind(std::forward<F>(f), std::forward<Args>(args)..., std::placeholders::_1)
114  );
115  {
116  ++_running;
117  std::unique_lock<std::mutex> lock(queue_mutex);
118  _tasks.push(task);
119  }
120  condition.notify_all();
121 }
122 
123 template<typename T>
124 void
126 {
127  {
128  std::unique_lock<std::mutex> lock(queue_mutex);
129  _stop = true;
130  }
131  condition.notify_all();
132  for(size_t i = 0; i<workers.size(); ++i)
133  workers[i].join();
134 }
135 
136 template<typename T>
137 void
139 {
140  if (!_stop)
141  _join();
142 }
143 
144 
145 template<typename T>
146 void
148 {
149  std::unique_lock<std::mutex> lock(_returned_mutex);
150  while(_running.load()!=0)
151  this->_returned.wait(lock);
152 }
153 
154 
155 // the destructor joins all threads if neccessary
156 template<typename T>
158 {
159  if (!_stop)
160  _join();
161 
162 }
163 
164 
165 
166 
167 #endif /* THREADPOOL2_HPP_ */