I am a beginner with threads and am trying to write code to extract 20 tags from a file. The number of files can run up to 7000, so I would like to make good use of a thread-pool.
I use Code::Blocks 20.3 and MinGW 17.1 on a Windows 10 Pro computer.
I have 'borrowed' the thread-pool code from: https://codereview.stackexchange.com/questions/221626/c17-thread-pool
I made a test that MinGW probably handles as C code, and that worked just fine.
My project involves multiple class files with dialog windows, and when I copied the working C code it fails to build. Unfortunately I do not understand how to convert the code from C to C++.
The test code I wrote is below.
The build messages are:
||=== Build: Debug in ThreadPool2 (compiler: GNU GCC Compiler) ===|
Threadpool.h||In instantiation of 'auto Thread_Pool::execute(F, Args&& ...) [with F = TrackTags::TagsStdStrings (TrackTags::*)(std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>); Args = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&}]':|
TrackTags.cpp|43|required from here|
Threadpool.h|62|error: no type named 'type' in 'struct std::invoke_result<TrackTags::TagsStdStrings (TrackTags::*)(std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>'|
Threadpool.h|63|error: no type named 'type' in 'struct std::invoke_result<TrackTags::TagsStdStrings (TrackTags::*)(std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>'|
Threadpool.h|62|error: no type named 'type' in 'struct std::invoke_result<TrackTags::TagsStdStrings (TrackTags::*)(std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>'|
Threadpool.h|63|error: no type named 'type' in 'struct std::invoke_result<TrackTags::TagsStdStrings (TrackTags::*)(std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>'|
TrackTags.cpp||In member function 'void TrackTags::GetMultiStdTags()':|
TrackTags.cpp|43|error: invalid use of void expression|
||=== Build failed: 5 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|
the lines with errors are:
TrackTags.cpp
[43] StdFutures.push_back(Pool.execute(GetStdTags, wsFile, wsCol));
Threadpool.h
[62] std::packaged_task<std::invoke_result_t<F, Args...>()> Task_PKG(std::bind(function, args...) );
[63] std::future<std::invoke_result_t<F, Args...>> Future = Task_PKG.get_future();
in Threadpool.h.
I tried:
[43] StdFutures.push_back(Pool.execute(std::mem_fn(TrackTags::GetStdTags), std::ref(wsFile), std::ref(wsCol)));
But this did not help.
I hope someone can help me make this work. Thank you.
Ruud.
---TrackTags.h---
#ifndef TRACKTAGS_H
#define TRACKTAGS_H
#include "Threadpool.h"
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
class TrackTags
{
public:
struct TagsStdStrings
{
bool OK;
std::string ThreadID;
std::string FileName;
std::string Collection;
};
public:
TrackTags();
virtual ~TrackTags();
TagsStdStrings GetStdTags(std::string wsFile, std::string wsCollection);
void GetMultiStdTags();
protected:
private:
};
#endif // TRACKTAGS_H
---TrackTags.cpp---
#include "TrackTags.h"
#define _UNICODE
TrackTags::TrackTags()
{
//ctor
}
TrackTags::~TrackTags()
{
//dtor
}
TrackTags::TagsStdStrings TrackTags::GetStdTags(std::string wsFile, std::string wsCollection)
{
TagsStdStrings TagLine;
TagLine.FileName = wsFile;
TagLine.Collection = wsCollection;
TagLine.OK = true;
// Add thread-ID to the structure
auto tid = std::this_thread::get_id();
std::stringstream ssID;
ssID << tid;
std::string sID{ssID.str()};
TagLine.ThreadID = sID;
return TagLine;
}
void TrackTags::GetMultiStdTags()
{
Thread_Pool Pool(1);
std::vector<std::future<TagsStdStrings>> StdFutures;
std::string wsFile{"FileTest"};
std::string wsCol{"ColTest"};
StdFutures.push_back(Pool.execute(GetStdTags, wsFile, wsCol));
for (auto &Fut : StdFutures)
{
TagsStdStrings TSS;
TSS = Fut.get();
if (TSS.OK)
{ std::cout << TSS.ThreadID << "--" << TSS.FileName << "--" << TSS.Collection << std::endl; }
else
{ std::cout << "Empty Tag structure\n"; }
}
}
---Threadpool.h---
#pragma once
#include <condition_variable>
#include <functional> //bind
#include <future> //packaged_task
#include <mutex>
#include <queue>
#include <thread>
#include <type_traits> //invoke_result
#include <vector>
class Thread_Pool
{
public:
Thread_Pool(size_t Thread_Count);
~Thread_Pool();
Thread_Pool(const Thread_Pool &) = delete;
Thread_Pool &operator=(const Thread_Pool &) = delete;
template <typename F, typename ...Args>
auto execute(F, Args&&...);
private:
class Task_Container_Base
{
public:
virtual ~Task_Container_Base() {};
virtual void operator()() = 0;
};
template <typename F>
class Task_Container : public Task_Container_Base
{
public:
Task_Container(F &&Fnc) : f(std::forward<F>(Fnc)) {}
void operator()() override { f(); }
private:
F f;
};
template <typename Func>
static std::unique_ptr<Task_Container_Base> Allocate_Task_Container(Func &&f)
{
return std::unique_ptr<Task_Container_Base>(new Task_Container<Func>(std::forward<Func>(f))
);
}
std::vector<std::thread> Threads;
std::queue<std::unique_ptr<Task_Container_Base>> Tasks;
std::mutex Task_Mutex;
std::condition_variable Task_CV;
bool Stop_Threads = false;
};
template <typename F, typename ...Args>
auto Thread_Pool::execute(F function, Args &&...args)
{
std::unique_lock<std::mutex> Queue_Lock(Task_Mutex, std::defer_lock);
std::packaged_task<std::invoke_result_t<F, Args...>()> Task_PKG(std::bind(function, args...) );
std::future<std::invoke_result_t<F, Args...>> Future = Task_PKG.get_future();
Queue_Lock.lock();
Tasks.emplace(Allocate_Task_Container( [Task(std::move(Task_PKG))]() mutable { Task(); }) );
Queue_Lock.unlock();
Task_CV.notify_one();
return Future;
}
---Threadpool.cpp---
#include "Threadpool.h"
Thread_Pool::Thread_Pool(size_t Thread_Count)
{
for (size_t i = 0; i < Thread_Count; ++i)
{
Threads.emplace_back( std::thread( [&]()
{
std::unique_lock<std::mutex> Queue_Lock(Task_Mutex, std::defer_lock);
while (true)
{
Queue_Lock.lock();
Task_CV.wait( Queue_Lock, [&]() -> bool { return !Tasks.empty() || Stop_Threads; } );
if (Stop_Threads && Tasks.empty()) return;
auto Temp_Task = std::move(Tasks.front());
Tasks.pop();
Queue_Lock.unlock();
(*Temp_Task)();
}
} ) );
}
}
Thread_Pool::~Thread_Pool()
{
Stop_Threads = true;
Task_CV.notify_all();
for (std::thread &Thread : Threads)
{
Thread.join();
}
}