I'm unsure whether I should use a std::semaphore or std::condition variable to handle thread waiting in a threadpool system.
I think that there is a low risk of using the semaphore and incrementing it beyond its compiler specific ::max().
I'm using c++20
Some of the following code is somewhat pseudo-code, but illustrates the program structure.
class ThreadPool
{
private:
struct ThreadData {
threadsafe_queue < fn2::unique_function<void() > > ThreadTask; //library that makes moveable function
**std::std::counting_semaphore<1> ThreadSignal {0}; //This is what i'm unsure of**
}
std::vector<std::jthread> mThreads;
std::vector<ThreadData> mThreadData;
template < typename Func >
void CreateTask(Func&& func) {
//iterate over threads and distribute the tasks
...
mThreadData[thread_id].ThreadTask.emplace_back(std::move(func));
mThreadData[thread_id].ThreadSignal.release(); //tell the queue there is work to do
}
public:
//Functions that parse a function into the above CreateTask()
...
ThreadPool() {
//for the required number of threads create them with an internal lambda that handles the work
...
mThreads.emplace_back(std::move(std::jthread([id = thread_id]() {
while (running) {
**mThreadData[id].ThreadSignal.aquire(); //gets the go-signal, which is still the focus of this post**
//handle work, either by getting from internal queue or stealing other work
//thread might "over"-consume the work in general by stealing work, but if a thread runs through this part without any work to get it will eventually run into a semaphore block
}
}
}
}
If for example the program allocates 1000 tasks to 2 threads. The main thread will quickly increase the signal semaphore to a very high number. I'm unsure if this is risking undefined behaviour? Should I implement a condition-variable instead? The semaphores should be faster than condition varibles in general, which is a reason to want to keep it. It's also really simple, since i can easily activate all theads by iterating over the semaphores and calling release on them all.
Previously I've had a simple this_thread::yield() call if no task gets pop'ed off the list, but it ends up just consuming a lot of cpu-time, which is the reason why I'd like the threads to "pause" and use a gateway of sorts.
Please bear with me, if I have misunderstood some concept, since I'm still a newbie.