You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
2.4 KiB

  1. #pragma once
  2. #include "unbounded_queue.hpp"
  3. #include "bounded_queue.hpp"
  4. #include <tuple>
  5. #include <atomic>
  6. #include <vector>
  7. #include <thread>
  8. #include <memory>
  9. #include <future>
  10. #include <utility>
  11. #include <functional>
  12. #include <type_traits>
  13. class ThreadPool
  14. {
  15. public:
  16. explicit
  17. ThreadPool(const std::size_t thread_count_ = std::thread::hardware_concurrency())
  18. : _queues(thread_count_),
  19. _count(thread_count_)
  20. {
  21. auto worker = [this](std::size_t i)
  22. {
  23. while(true)
  24. {
  25. Proc f;
  26. for(std::size_t n = 0; n < (_count * K); ++n)
  27. {
  28. if(_queues[(i + n) % _count].try_pop(f))
  29. break;
  30. }
  31. if(!f && !_queues[i].pop(f))
  32. break;
  33. f();
  34. }
  35. };
  36. _threads.reserve(thread_count_);
  37. for(std::size_t i = 0; i < thread_count_; ++i)
  38. _threads.emplace_back(worker, i);
  39. }
  40. ~ThreadPool()
  41. {
  42. for(auto& queue : _queues)
  43. queue.unblock();
  44. for(auto& thread : _threads)
  45. thread.join();
  46. }
  47. template<typename F>
  48. void
  49. enqueue_work(F&& f_)
  50. {
  51. auto i = _index++;
  52. for(std::size_t n = 0; n < (_count * K); ++n)
  53. {
  54. if(_queues[(i + n) % _count].try_push(f_))
  55. return;
  56. }
  57. _queues[i % _count].push(std::move(f_));
  58. }
  59. template<typename F>
  60. [[nodiscard]]
  61. std::future<typename std::result_of<F()>::type>
  62. enqueue_task(F&& f_)
  63. {
  64. using TaskReturnType = typename std::result_of<F()>::type;
  65. using Promise = std::promise<TaskReturnType>;
  66. auto i = _index++;
  67. auto promise = std::make_shared<Promise>();
  68. auto future = promise->get_future();
  69. auto work = [=]() {
  70. auto rv = f_();
  71. promise->set_value(rv);
  72. };
  73. for(std::size_t n = 0; n < (_count * K); ++n)
  74. {
  75. if(_queues[(i + n) % _count].try_push(work))
  76. return future;
  77. }
  78. _queues[i % _count].push(std::move(work));
  79. return future;
  80. }
  81. public:
  82. std::vector<pthread_t>
  83. threads()
  84. {
  85. std::vector<pthread_t> rv;
  86. for(auto &thread : _threads)
  87. rv.push_back(thread.native_handle());
  88. return rv;
  89. }
  90. private:
  91. using Proc = std::function<void(void)>;
  92. using Queue = UnboundedQueue<Proc>;
  93. using Queues = std::vector<Queue>;
  94. Queues _queues;
  95. private:
  96. std::vector<std::thread> _threads;
  97. private:
  98. const std::size_t _count;
  99. std::atomic_uint _index;
  100. static const unsigned int K = 2;
  101. };