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();
}
}
}
}