提问者:小点点

模板类内部静态成员的惰性初始化


嗨,我是C++方面的新手,我正在尝试实现一个线程池,这是因为我正在尝试修正一些C++概念。 下面是我目前所写的内容:

#include <condition_variable>
#include <functional>
#include <future>
#include <mutex>
#include <optional>
#include <queue>

/**
 * This class should be a singleton, that's because if
 * we want to limit the amount of running threads
 * we should check that there are not multiple instances
 * of this class. This implementation provides guarantees
 * that there will be a single instance of a given template
 * specialization. This class provide also a thread safe queue.
 */

template <typename R, typename... Args> class ThreadPool {

private:
  std::mutex m;
  std::condition_variable cv;
  bool is_running;
  size_t pool_size;
  std::queue<std::packaged_task<R(Args...)>> jobs;
  static ThreadPool<R, Args...> *instance; // Singleton instance
  ThreadPool()
      : is_running{true}, pool_size{std::thread::hardware_concurrency()} {
  } // the constructor should not be accessible
  ThreadPool(size_t pool_size)
      : is_running{true}, pool_size{pool_size} {
  } // the constructor should not be accessible

public:
  ThreadPool(const ThreadPool &) = delete; // copy constructor disabled;
  ThreadPool &
  operator=(const ThreadPool &) = delete; // copy assignment disabled
  ThreadPool &
  operator=(ThreadPool &&other) = delete; // movement temporary disabled

  /**
   * Create a thred pool with a size that it's equals to
   * the number of cores on the machine. This istance
   * should be use to maximize the parallelism.
   */
  static ThreadPool<R, Args...> *getInstance() {
    if (instance == nullptr)
      instance = new ThreadPool<R, Args...>{};
    return ThreadPool::instance;
  }

  /**
   * Create a thred pool with a size that it's equals to
   * the given pool size. This istance should be use
   * when we don't need to use the highest level of
   * parallelism.
   *
   * @pool_size: desired size of the thread pool
   */

  static ThreadPool<R, Args...> *getInstance(const size_t pool_size) {
    if (ThreadPool::instance == nullptr)
      ThreadPool::instance = new ThreadPool<R, Args...>{pool_size};
    return ThreadPool::instance;
  }

  void submit(std::packaged_task<R(Args...)> f) {
    std::unique_lock l{m};
    if (is_running) {
      if (jobs.size() == pool_size)
        cv.wait(l, [&]() { return jobs.size() < pool_size; });
      jobs.push(std::move(f));
      cv.notify_one();
    }
  }

  std::optional<std::packaged_task<R(Args...)>> get() {
    std::unique_lock l{m};
    if (jobs.size() == 0 && is_running)
      cv.wait(l, [&]() { return jobs.size() > 0 || !is_running; });
    if (jobs.size() > 0 && is_running) {
      std::packaged_task<R(Args...)> f = std::move(jobs.front());
      cv.notify_one();
      return std::optional<std::packaged_task<R(Args...)>>{std::move(f)};
    }
    return std::nullopt;
  }

  void quit() {
    // todo: valutare eccezione su quit multiple
    std::unique_lock l{m};
    if (is_running) {
      is_running = false;
      cv.notify_all();
    }
  }
};

int main() {

  static ThreadPool<int, int, int> *t = ThreadPool<int, int, int>::getInstance();
 
  return 0;
}

当我编译:g++-g-wall-werror-pthread-std=C++17 main.cpp-o main时,会出现以下错误:

/usr/bin/ld: /tmp/cc6zwABg.o: in function `ThreadPool<int, int, int>::getInstance()':
/home/gjcode/Politecnico/CodeProjects/PDS/cpp/threadpool/threadpool.h:54: undefined reference to `ThreadPool<int, int, int>::instance'
/usr/bin/ld: /home/gjcode/Politecnico/CodeProjects/PDS/cpp/threadpool/threadpool.h:55: undefined reference to `ThreadPool<int, int, int>::instance'
/usr/bin/ld: /home/gjcode/Politecnico/CodeProjects/PDS/cpp/threadpool/threadpool.h:56: undefined reference to `ThreadPool<int, int, int>::instance'
collect2: error: ld returned 1 exit status

它似乎与链接阶段有关,我已经尝试使用cppInsights来查看tamplate是如何初始化的,它似乎可以正常工作。 为什么我不能访问静态成员实例?? 我也尝试移动公共区域的领域,但它没有工作。


共1个答案

匿名用户

您忘记在类定义之外定义实例:

template <typename R, typename... Args>
ThreadPool<R, Args...>* ThreadPool<R, Args...>::instance = nullptr;

观点:一个实例有两种创建方式是很奇怪的。 如果按以下顺序进行呼叫:

static ThreadPool<R, Args...> *getInstance();
static ThreadPool<R, Args...> *getInstance(const size_t pool_size);

。。。您将得到一个默认的pool_size,如果您使用另一种方式,您将得到一个用户定义的pool_size。 我建议选择其中一个,并将实例移到实际的getInstance()方法中。 您可以将pool_size设置为模板参数,而不是使用两种方法实例化单例。

如果你想保留两种构造方法,你可以这样做:

static ThreadPool<R, Args...>& getInstance(const size_t pool_size) {
    static ThreadPool<R, Args...> instance(pool_size);
    return instance;
}
static ThreadPool<R, Args...>& getInstance() {
    return getInstance(std::thread::hardware_concurrency());
}

请注意,它将返回一个引用。 这种方式有一些好处:

  • 以后不需要对其进行删除(您在当前实现中也忘记了这一点)。
  • 您可以删除默认构造函数。
  • 不需要检查它是否为nullptr
  • 它是线程安全的(您当前的实现不是这样)。