【问题标题】:Attempt to use a deleted function with C++11 threads尝试在 C++11 线程中使用已删除的函数
【发布时间】:2020-03-26 07:43:45
【问题描述】:

我正在尝试创建一个具有这样的构造函数的 ThreadPool 类

    ThreadPool(size_t numberOfThreads)
    : workerThreads(numberOfThreads) {
      workerThreads.reserve(numberOfThreads);
      for(int i =0; i < numberOfThreads; i++) {
        workerThreads.emplace_back(std::thread(&ThreadPool::doJob, this));
      }
    }

这无法为我编译并引发以下错误

error: attempt to use a deleted function
    __invoke(_VSTD::move(_VSTD::get<1>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);
    ^
/Library/Developer/CommandLineTools/usr/include/c++/v1/thread:352:5: note: in instantiation of function template specialization
      'std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void
      (ThreadPool::*)(std::__1::function<void (char *, std::__1::list<std::__1::basic_string<char>,
      std::__1::allocator<std::__1::basic_string<char> > > *)>), ThreadPool *, 2>' requested here
    __thread_execute(*__p, _Index());

根据类似问题的其他答案,我也尝试过这样做

        workerThreads.emplace_back(std::thread(&ThreadPool::doJob, std::ref(*this)));

这也重现了同样的问题。我在 macos 上使用 clang c++14 编译我的代码

这是一个完整的程序,它再现了

class ThreadPool {
  public:
    ThreadPool(size_t numberOfThreads)
    : workerThreads(numberOfThreads) {
      for(int i =0; i < numberOfThreads; i++) {
        workerThreads[i] = std::make_shared<std::thread>(std::thread(&ThreadPool::doJob, this));
      }
    }

    ThreadPool(const ThreadPool& tp) {
      workerThreads = tp.workerThreads;
      jobQueue = tp.jobQueue;
    }

    std::function<void(char*, std::list<std::string>*)> getNextJob() {
      if(!jobQueue.empty()) {
        std::function<void(char*, std::list<std::string>*)> job = jobQueue.front();
        jobQueue.pop_front();
        return job;
      } 

      throw std::runtime_error("No jobs to process, thread finished");
    }

    void addWork(std::function<void(char*, std::list<std::string>*)> job) {
      lockListMutex.lock();
      jobQueue.push_back(job);
      lockListMutex.unlock();
    }

  private:
    // performs actual work
    void doJob(std::function<void(char*, std::list<std::string>*)> job) {
      try {
        lockListMutex.lock();
        getNextJob();
        lockListMutex.unlock();
      } catch (std::runtime_error &e) {

      }
    }

    // a vector containing worker threads
    std::vector<std::shared_ptr<std::thread>> workerThreads;

    // a queue for jobs
    std::list<std::function<void(char*, std::list<std::string>*)>> jobQueue;

    // a mutex for synchronized insertion and deletion from list
    std::mutex lockListMutex;
};

int main(int argc, char *argv[]) {
    int numThreads = 1;
    ThreadPool* pool = new ThreadPool(numThreads);
}

【问题讨论】:

  • 您使用的是哪种容器?如果它是一个向量,则假定底层类型(恰好是 std::thread)需要是可复制构造的,而这恰好是不允许被复制的。
  • 是的,它是std::vector
  • 您可以使用指针向量,因为允许复制 std::thread 指针。但是,在向量销毁时加入或分离所有线程将取决于您。
  • 顺便说一句,在您的构造函数中,您正在创建 numberOfThreads * 2 的线程。此外,您的“保留”没有任何意义,因为您已经实例化了一个相同大小的向量。它不会为您分配更多内存。并且重新放置会为您的向量添加两倍的元素。假设您使用可复制构造的类,则需要使用ThreadPool(size_t size) : workerThreads(size, std::thread(&amp;ThreadPool::doJob, this)) {}
  • 我经常在 macOS 平台上使用std::vector&lt;std::thread&gt;,并且已经使用了多年。我看到的唯一奇怪的事情是除了没有minimal reproducible example 的时间量是您的成员初始化列表中的workerThreads(size),这显然是错误的。 workerThreads(size, std::thread(&amp;ThreadPool::doJob, this)) 不会,这需要 std::thread 是可复制的,我们已经知道它不是(也不是可复制分配的)。但是,它应该是可移动的。

标签: c++ multithreading c++11 c++14


【解决方案1】:

由于您的doJob 成员函数有一个job 类型为std::function&lt;void(char*, std::list&lt;std::string&gt;*)&gt; 的参数,因此您需要在调用此函数时提供相应的参数。因此,以这种方式在新线程中调用该函数是无效的:

std::thread(&ThreadPool::doJob, this)  // no argument for job passed

您要么需要提供一个参数,要么因为作业可能会从jobQueue 中出列,所以从doJob 中删除job 参数:

void doJob()
{
   ... // dequeue job from jobQueue and execute it

【讨论】:

  • 确实如此。 proc() 是我的工作队列线程 procs 的样子。我的队列通常是std::queue&lt;std::function&lt;void()&gt;&gt;,并且我模板化了一个推送成员,它采用可变参数 args 来通过一个老式但好东西 std::bind 再次转发 fn 可调用和转发可变参数参数(如果有)。这就是队列中的内容,最终在线程池中被拉出并触发。注意到doJob args 很好。道具。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-25
  • 1970-01-01
  • 2016-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多