mtrebi / thread-pool

Thread pool implementation using c++11 threads

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug report: When sub-task are performed quickly, the results are inaccurate

muyuuuu opened this issue · comments

#include <iostream>

#include "../include/ThreadPool.h"

class TEST {
private:
  std::mutex mtx;
  std::vector<int> v1[2];
  std::priority_queue<int> q;
public:
  void init() {
    v1[0].push_back(1);
    v1[0].push_back(2);
    v1[0].push_back(3);

    v1[1].push_back(1);
    v1[1].push_back(3);
    v1[1].push_back(2);
  }
  
  void push(const int& index) {
    for (auto& i : v1[index]) {
      std::lock_guard<std::mutex> m{mtx};
      q.push(i);
    }
  }

  void run() {
    ThreadPool pool(2);
    pool.init();
    // https://github.com/mtrebi/thread-pool/issues/14
    // https://github.com/mtrebi/thread-pool/issues/7
    for (int i = 0; i < 100; i++) {
      for (int j = 0; j < 2; j++) {
        auto func = std::bind(&TEST::push, this, j);
        pool.submit(func);
      }
    }
    pool.shutdown();

    int tmp{-2};
    std::cout << q.size() << std::endl;
  }
};

int main(int argc, char *argv[]) {
  TEST t;
  t.init();
  t.run();
  return 0;
}

The output of the above code is not accurate.

315 // sometimes
291 // sometimes
69 // sometimes

If I add thread-sleep, the results will be 6.

void push(const int& index) {
  std::this_thread::sleep_for(std::chrono::milliseconds(2000));
  for (auto& i : v1[index]) {
    std::lock_guard<std::mutex> m{mtx};
    q.push(i);
  }
}

Is there some bugs?

when pool.shutdown() was runned, I found that m_queue.size() isn't equal to zero. How to fix it up? I'll try to fix it.

I wrote an inelegant solution. When the thread pool is closed, the task queue is checked to see if there are any tasks left. If so, execution continues.

void shutdown() {
  m_shutdown = true;
  m_conditional_lock.notify_all();
  for (int i = 0; i < m_threads.size(); ++i) {
    if(m_threads[i].joinable()) {
      m_threads[i].join();
    }
  }

  bool dequeued;
  std::function<void()> func;
  while (m_queue.size()) {
    {
      std::unique_lock<std::mutex> lock(m_conditional_mutex);
      dequeued = m_queue.dequeue(func);
    }
    if (dequeued) {
      func();
    }
  }
}

But this would lose the performance benefits of multithreading. Is there a better solution?

OK, I think this plan is better. I will send pull request, Thank you for writing such good code that I can use in my application program.

void operator()() {
  std::function<void()> func;
  bool dequeued;
  while (!m_pool->m_shutdown) {
    {
      std::unique_lock<std::mutex> lock(m_pool->m_conditional_mutex);
      if (m_pool->m_queue.empty()) {
        m_pool->m_conditional_lock.wait(lock);
      }
      dequeued = m_pool->m_queue.dequeue(func);
    }
    if (dequeued) {
      func();
    }
  }

  // If the task queue is not empty, continue obtain task from task queue, 
  // the multithread continues execution until the queue is empty
  while (!m_pool->m_queue.empty()) {
    {
      std::unique_lock<std::mutex> lock(m_pool->m_conditional_mutex);
      dequeued = m_pool->m_queue.dequeue(func);
      if (dequeued) {
        func();
      }
    }
  }
}