Non-blocking semaphores in C++11? - c++

A number of questions on this site deal with the lack of a semaphore object in the multi-threading support introduced in C++11. Many people suggested implementing semaphores using mutexes or condition variables or a combination of both.
However, none of these approaches allows to increment and decrement a semaphore while guaranteeing that the calling thread is not blocked, since usually a lock must be acquired before reading the semaphore's value. The POSIX semaphore for instance has the functions sem_post() and sem_trywait(), both of which are non-blocking.
Is it possible to implement a non-blocking semaphore with the C++11 multi-threading support only? Or am I necessarily required to use an OS-dependent library for this? If so, why does the C++11 revision not include a semaphore object?
A similar question has not been answered in 3 years. (Note: I believe the question I am asking is much broader though, there are certainly other uses for a non-blocking semaphore object aside from a producer/consumer. If despite this someone believes my question is a duplicate, then please tell me how I can bring back attention to the old question since this is still an open issue.)

I don't see a problem to implement a semaphore. Using C++11 atomics and mutextes it should be possible.
class Semaphore
{
private:
std::atomic<int> count_;
public:
Semaphore() :
count_(0) // Initialized as locked.
{
}
void notify() {
count_++;
}
void wait() {
while(!try_wait()) {
//Spin Locking
}
}
bool try_wait() {
int count = count_;
if(count) {
return count_.compare_exchange_strong(count, count - 1);
} else {
return false;
}
}
};
Here is a little example of the usage:
#include <iostream>
#include "Semaphore.hpp"
#include <thread>
#include <vector>
Semaphore sem;
int counter;
void run(int threadIdx) {
while(!sem.try_wait()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
//Alternative use wait
//sem.wait()
std::cout << "Thread " << threadIdx << " enter critical section" << std::endl;
counter++;
std::cout << "Thread " << threadIdx << " incresed counter to " << counter << std::endl;
// Do work;
std::this_thread::sleep_for(std::chrono::milliseconds(30));
std::cout << "Thread " << threadIdx << " leave critical section" << std::endl;
sem.notify();
}
int main() {
std::vector<std::thread> threads;
for(int i = 0; i < 15; i++) {
threads.push_back(std::thread(run, i));
}
sem.notify();
for(auto& t : threads) {
t.join();
}
std::cout << "Terminate main." << std::endl;
return 0;
}
Of course, the wait is a blocking operation. But notify and try_wait are both non-blocking, if the compare and exchange operation is non blocking (can be checked).

Related

Multiple threads waiting for all to finish till new work is started

I am trying to create a sort of threadpool that runs functions on separate threads and only starts a new iteration when all functions have finished.
map<size_t, bool> status_map;
vector<thread> threads;
condition_variable cond;
bool are_all_ready() {
mutex m;
unique_lock<mutex> lock(m);
for (const auto& [_, status] : status_map) {
if (!status) {
return false;
}
}
return true;
}
void do_little_work(size_t id) {
this_thread::sleep_for(chrono::seconds(1));
cout << id << " did little work..." << endl;
}
void do_some_work(size_t id) {
this_thread::sleep_for(chrono::seconds(2));
cout << id << " did some work..." << endl;
}
void do_much_work(size_t id) {
this_thread::sleep_for(chrono::seconds(4));
cout << id << " did much work..." << endl;
}
void run(const function<void(size_t)>& function, size_t id) {
while (true) {
mutex m;
unique_lock<mutex> lock(m);
cond.wait(lock, are_all_ready);
status_map[id] = false;
cond.notify_all();
function(id);
status_map[id] = true;
cond.notify_all();
}
}
int main() {
threads.push_back(thread(run, do_little_work, 0));
threads.push_back(thread(run, do_some_work, 1));
threads.push_back(thread(run, do_much_work, 2));
for (auto& thread : threads) {
thread.join();
}
return EXIT_SUCCESS;
}
I expect to get the output:
0 did little work...
1 did some work...
2 did much work...
0 did little work...
1 did some work...
2 did much work...
.
.
.
after the respective timeouts but when I run the program I only get
0 did little work...
0 did little work...
.
.
.
I also have to say that Im rather new to multithreading but in my understanding, the condition_variable should to the taks of blocking every thread till the predicate returns true. And in my case are_all_ready should return true after all functions have returned.
There are several ways to do this.
Easiest in my opinion would be a C++20 std::barrier, which says, "wait until all of N threads have arrived and are waiting here."
#include <barrier>
std::barrier synch_workers(3);
....
void run(const std::function<void(size_t)>& func, size_t id) {
while (true) {
synch_workers.arrive_and_wait(); // wait for all three to be ready
func(id);
}
}
Cruder and less efficient, but equally effective, would be to construct and join() new sets of three worker threads for each "batch" of work:
int main(...) {
std::vector<thread> threads;
...
while (flag_running) {
threads.push_back(...);
threads.push_back(...);
...
for (auto& thread : threads) {
thread.join();
}
threads.clear();
}
Aside
I'd suggest you revisit some core synchronization concepts, however. You are using new mutexes when you want to re-use a shared one. The scope of your unique_lock isn't quite right.
Now, your idea to track worker thread "busy/idle" state in a map is straightforward, but cannot correctly coordinate "batches" or "rounds" of work that must be begun at the same time.
If a worker sees in the map that two of three threads, including itself, are "idle", what does that mean? Is a "batch" of work concluding — i.e., two workers are waiting for a tardy third? Or has a batch just begun — i.e., the two idle threads are tardy and had better get to work like their more eager peer?
The threads cannot know the answer without keeping track of the current batch of work, which is what a barrier (or its more complex cousin the phaser) does under the hood.
As-is, your program has a crash (UB) due to concurrent access to status_map.
When you do:
void run(const function<void(size_t)>& function, size_t id)
{
...
mutex m;
unique_lock<mutex> lock(m);
...
status_map[id] = false;
the locks created are local variables, one per thread, and as such independent. So, it doesn't prevent multiple threads from writing to status_map at once, and thus crashing. That's what I get on my machine.
Now, if you make the mutex static, only one thread can access the map at once. But that also makes it so that only one thread runs at once. With this I see 0, 1 and 2 running, but only once at a time and a strong tendency for the previous thread to have run to run again.
My suggestion, go back to the drawing board and make it simpler. All threads run at once, single mutex to protect the map, only lock the mutex to access the map, and ... well, in fact, I don't even see the need for a condition variable.
e.g. what is wrong with:
#include <thread>
#include <iostream>
#include <vector>
using namespace std;
vector<thread> threads;
void do_little_work(size_t id) {
this_thread::sleep_for(chrono::seconds(1));
cout << id << " did little work..." << endl;
}
void do_some_work(size_t id) {
this_thread::sleep_for(chrono::seconds(2));
cout << id << " did some work..." << endl;
}
void do_much_work(size_t id) {
this_thread::sleep_for(chrono::seconds(4));
cout << id << " did much work..." << endl;
}
void run(const function<void(size_t)>& function, size_t id) {
while (true) {
function(id);
}
}
int main() {
threads.push_back(thread(run, do_little_work, 0));
threads.push_back(thread(run, do_some_work, 1));
threads.push_back(thread(run, do_much_work, 2));
for (auto& thread : threads) {
thread.join();
}
return EXIT_SUCCESS;
}

Using boost to turn single thread to multi thread

I'm trying to turn a code from a single thread to a multi thread(example, create 6 threads instead of 1) while making sure they all start and finish without any interference from each other. What would be a way to do this? Could I just do a for loop that creates a thread until i < 6? And just add a mutex class with lock() and unlock()?
#include <iostream>
#include <boost/thread.hpp>
#include <boost/date_time.hpp>
void workerFunc()
{
boost::posix_time::seconds workTime(3);
std::cout << "Worker: running" << std::endl;
// Pretend to do something useful...
boost::this_thread::sleep(workTime);
std::cout << "Worker: finished" << std::endl;
}
int main(int argc, char* argv[])
{
std::cout << "main: startup" << std::endl;
boost::thread workerThread(workerFunc);
std::cout << "main: waiting for thread" << std::endl;
workerThread.join();
std::cout << "main: done" << std::endl;
system("pause");
return 0;
}
Yes, it's certainly possible. Since you don't want any interference between them, give them unique data to work with so that you do not need to synchronize the access to that data with a std::mutex or making it std::atomic. To further minimize the interference between threads, align the data according to std::hardware_destructive_interference_size.
You can use boost::thread::hardware_concurrency() to get the number of hardware threads available on the current system so that you don't have to hardcode the number of threads to run.
Passing references to the thread can be done using std::ref (or else the thread will get a ref to a copy of the data).
Here I create a std::list of threads and a std::vector of data to work on.
#include <cstdint> // std::int64_t
#include <iostream>
#include <list>
#include <new> // std::hardware_destructive_interference_size
#include <vector>
#include <boost/thread.hpp>
unsigned hardware_concurrency() {
unsigned rv = boost::thread::hardware_concurrency();
if(rv == 0) rv = 1; // fallback if hardware_concurrency returned 0
return rv;
}
// if you don't have hardware_destructive_interference_size, use something like this
// instead:
//struct alignas(64) data {
struct alignas(std::hardware_destructive_interference_size) data {
std::int64_t x;
};
void workerFunc(data& d) {
// work on the supplied data
for(int i = 0; i < 1024*1024-1; ++i) d.x -= i;
for(int i = 0; i < 1024*1024*1024-1; ++i) d.x += i;
}
int main() {
std::cout << "main: startup" << std::endl;
size_t number_of_threads = hardware_concurrency();
std::list<boost::thread> threads;
std::vector<data> dataset(number_of_threads);
// create the threads
for(size_t idx = 0; idx < number_of_threads; ++idx)
threads.emplace_back(workerFunc, std::ref(dataset[idx]));
std::cout << "main: waiting for threads" << std::endl;
// join all threads
for(auto& th : threads) th.join();
// display results
for(const data& d : dataset) std::cout << d.x << "\n";
std::cout << "main: done" << std::endl;
}
If you are using C++11 (or later), I suggest using std::thread instead.
Starting and stopping a bunch of Boost threads
std::vector<boost::thread> threads;
for (int i = 0; i < numberOfThreads; ++i) {
boost::thread t(workerFunc);
threads.push_back(std::move(t));
}
for (auto& t : threads) {
t.join();
}
Keep in mind that join() doesn't terminate the threads, it only waits until they are finished.
Synchronization
Mutexes are required if multiple threads access the same data and at least one of them is writing the data. You can use a mutex to ensure that multiple threads enter the critical sections of the code. Example:
std::queue<int> q;
std::mutex q_mu;
void workerFunc1() {
// ...
{
std::lock_guard<std::mutex> guard(q_mu);
q.push(foo);
} // lock guard goes out of scope and automatically unlocks q_mu
// ...
}
void workerFunc2() {
// ...
{
std::lock_guard<std::mutex> guard(q_mu);
foo = q.pop();
} // lock guard goes out of scope and automatically unlocks q_mu
// ...
}
This prevents undefined behavior like reading an item from the queue that hasn't been written completely. Be careful - data races can crash your program or corrupt your data. I'm frequently using tools like Thread Sanitizer or Helgrind to ensure I didn't miss anything. If you only want to pass results back into the main program but don't need to share data between your threads you might want to consider using std::promise and std::future.
Yes, spawning new threads can be done with a simple loop. You will have to keep a few things in mind though:
If threads will operate on shared data, it will need to be protected with mutexes, atomics or via some other way to avoid data races and undefined behaviour (bear in mind that even primitive types such as int have to be wrapped with an atomic or mutex according to the standard).
You will have to make sure that you will eventually either call join() or detach() on every spawned thread before its object goes out of scope to prevent it from suddenly terminating.
Its best to do some computations on the main thread while waiting for worker threads to use this time efficiently instead of wasting it.
You generally want to spawn 1 thread less than the number of total threads you want as the program starts running with with one thread by default (the main thread).

Mutex does not work as expected

I have used mutex in inherited classes but seems it does not work as I expected with threads. Please have a look at below code:
#include <iostream>
#include <cstdlib>
#include <pthread.h>
// mutex::lock/unlock
#include <iostream> // std::cout
#include <thread> // std::thread
#include <chrono> // std::thread
#include <mutex> // std::mutex
typedef unsigned int UINT32t;
typedef int INT32t;
using namespace std;
class Abstract {
protected:
std::mutex mtx;
};
class Derived: public Abstract
{
public:
void* write( void* result)
{
UINT32t error[1];
UINT32t data = 34;
INT32t length = 0;
static INT32t counter = 0;
cout << "\t before Locking ..." << " in thread" << endl;
mtx.lock();
//critical section
cout << "\t After Create " << ++ counter << " device in thread" << endl;
std::this_thread::sleep_for(1s);
mtx.unlock();
cout << "\t deallocated " << counter << " device in thread" << endl;
pthread_exit(result);
}
};
void* threadTest1( void* result)
{
Derived dev;
dev.write(nullptr);
}
int main()
{
unsigned char byData[1024] = {0};
ssize_t len;
void *status = 0, *status2 = 0;
int result = 0, result2 = 0;
pthread_t pth, pth2;
pthread_create(&pth, NULL, threadTest1, &result);
pthread_create(&pth2, NULL, threadTest1, &result2);
//wait for all kids to complete
pthread_join(pth, &status);
pthread_join(pth2, &status2);
if (status != 0) {
printf("result : %d\n",result);
} else {
printf("thread failed\n");
}
if (status2 != 0) {
printf("result2 : %d\n",result2);
} else {
printf("thread2 failed\n");
}
return -1;
}
so the result is:
*Four or five arguments expected.
before Locking ... in thread
After Create 1 device in thread
before Locking ... in thread
After Create 2 device in thread
deallocated 2 device in thread
deallocated 2 device in thread
thread failed
thread2 failed
*
So here we can see that second thread comes to critical section before mutex was deallocated.
The string "After Create 2 device in thread" says about that.
If it comes to critical section before mutex is deallocated it means mutex works wrong.
If you have any thoughts please share.
thanks
The mutex itself is (probably) working fine (I'd recommend you to use std::lock_guard though), but both threads create their own Derived object, hence, they don't use the same mutex.
Edit: tkausl's answer is correct -- however, even if you switch to using a global mutex, the output may not change because of the detail in my answer so I'm leaving it here. In other words, there are two reasons why the output may not be what you expect, and you need to fix both.
Note in particular these two lines:
mtx.unlock();
cout << "\t deallocated " << counter << " device in thread" << endl;
You seem to be under the impression that these two lines will be run one right after the other, but there is no guarantee that this will happen in a preemptive multithreading environment. What can happen instead is that right after mtx.unlock() there could be a context switch to the other thread.
In other words, the second thread is waiting for the mutex to unlock, but the first thread isn't printing the "deallocated" message before the second thread preempts it.
The simplest way to get the output you expect would be to swap the order of these two lines.
You shall declare your mutex as a global variable and initiate it before calling pthread_create. You created two threads using pthread_create and both of them create their own mutex so there is absolutely no synchronization between them.

How to find out if other threads are running?

I have a "watch thread" which checks whether other threads are running and calculates some data. If these threads end I want to finish my watch thread, too. How can I do it?
#include <iostream>
#include <thread>
using namespace std;
void f1() {
cout << "thread t1" << endl;
for (int i=0; i<1000; ++i) {
cout << "t1: " << i << endl;
}
}
void f2() {
cout << "thread t2" << endl;
while (T1_IS_RUNNING) {
cout << "t1 still running" << endl;
}
}
int main() {
thread t1(f1);
thread t2(f2);
t1.join();
t2.join();
return 0;
}
In the example above I need to implement T1_IS_RUNNING. Any ideas how to do it? My guess is to get number of running threads but I haven't found any related method in STL.
There is a How to check if a std::thread is still running? already, but I think they use too complicated solutions for my case. Isn't a simple thread counter (std::atomic) good enough?
You can just use a flag for it (running example):
#include <iostream>
#include <thread>
using namespace std;
bool T1_IS_RUNNING = true;
void f1() {
cout << "thread t1" << endl;
for (int i=0; i<1000; ++i) {
cout << "t1: " << i << endl;
}
T1_IS_RUNNING = false;
cout << "thread t1 finish" << endl;
}
void f2() {
cout << "thread t2" << endl;
while (T1_IS_RUNNING) {
cout << "t1 still running" << endl;
}
cout << "thread t2 finish" << endl;
}
int main() {
thread t1(f1);
thread t2(f2);
t1.join();
t2.join();
return 0;
}
This is safe as long as only one of them writes the flag and the other reads it, otherwise you need to use an atomic flag, a mutex or a semaphore.
With atomic_int:
int main(){
std::atomic_int poor_man_semaphore{0};
poor_man_semaphore++;
std::thread t1([&]()
{
std::this_thread::sleep_for(std::chrono::seconds(100));
poor_man_semaphore--;
});
poor_man_semaphore++;
std::thread t2([&]()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
poor_man_semaphore--;
});
poor_man_semaphore++;
std::thread t3([&]()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
poor_man_semaphore--;
});
t2.join();
t3.join();
while ( poor_man_semaphore > 0 )
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
t1.join();
return 0;
}
Let me give a quick fix to the code, as there is already a detailed post, this will not be long.
This answer exists because there are many wrong answers here.
My interpretation of your problem is you want a "watch thread" to do work while other threads are still alive, but stop whenever others stop.
#include <fstream>
#include <thread>
#include <atomic> // this is REQUIRED, NOT OPTIONAL
using namespace std;
atomic_int count(1); // REQUIRED to be atomic
void f1() {
ofstream f1out{"f1out.txt"};
f1out << "thread t1" << endl;
for (int i=0; i<1000; ++i) {
f1out << "t1: " << i << endl;
}
count--;
}
void f2() {
ofstream f2out{"f2out.txt"};
f2out << "thread t2" << endl;
while (count > 0) {
f2out << "t1 still running" << endl;
}
}
int main() {
thread t1(f1);
thread t2(f2);
t1.join();
t2.join();
}
Notes on atomic
The syntax of atomic_int might look like an int but they are different and failing to use atomic_int is undefined behaviour.
From [intro.races], emphasis mine
Two expression evaluations conflict if one of them modifies a memory location and the other one reads or modifies the same memory location. [...]
The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other [...] . Any such data race results in undefined behavior.
Notes on cout
Likewise, it is a data race if the threads use cout concurrently, I can't find a simple replacement to preserve the meaning and effect. I opt into using ofstream in the end.
For people concerned
Yes, the atomic operations need not be sequentially consistent but that really doesn't help with clarity.
This link might help you.
Amongst a lot of solutions, one seems quite easy to implement :
An easy solution is to have a boolean variable that the thread sets to true on regular intervals, and that is checked and set to false by the thread wanting to know the status. If the variable is false for to long then the thread is no longer considered active.
A more thread-safe way is to have a counter that is increased by the child thread, and the main thread compares the counter to a stored value and if the same after too long time then the child thread is considered not active.
May be you could set an array of boolean, one by thread you run, and then check it whenever you want to know if other threads are running ?

How to limit the number of running instances in C++

I have a c++ class that allocates a lot of memory. It does this by calling a third-party library that is designed to crash if it cannot allocate the memory, and sometimes my application creates several instances of my class in parallel threads. With too many threads I have a crash.
My best idea for a solution is to make sure that there are never, say, more than three instances running at the same time. (Is this a good idea?)
And my current best idea for implementing that is to use a boost mutex. Something along the lines of the following pseudo-code,
MyClass::MyClass(){
my_thread_number = -1; //this is a class variable
while (my_thread_number == -1)
for (int i=0; i < MAX_PROCESSES; i++)
if(try_lock a mutex named i){
my_thread_number = i;
break;
}
//Now I know that my thread has mutex number i and it is allowed to run
}
MyClass::~MyClass(){
release mutex named my_thread_number
}
As you see, I am not quite sure of the exact syntax for mutexes here.. So summing up, my questions are
Am I on the right track when I want to solve my memory error by limiting the number of threads?
If yes, Should I do it with mutexes or by other means?
If yes, Is my algorithm sound?
Is there a nice example somewhere of how to use try_lock with boost mutexes?
Edit: I realized I am talking about threads, not processes.
Edit: I am involved in building an application that can run on both linux and Windows...
UPDATE My other answer addresses scheduling resources among threads (after the question was clarified).
It shows both a semaphore approach to coordinate work among (many) workers, and a thread_pool to limit workers in the first place and queue the work.
On linux (and perhaps other OSes?) you can use a lock file idiom (but it's not supported with some file-systems and old kernels).
I would suggest to use Interprocess synchronisation objects.
E.g., using a Boost Interprocess named semaphore:
#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>
int main()
{
using namespace boost::interprocess;
named_semaphore sem(open_or_create, "ffed38bd-f0fc-4f79-8838-5301c328268c", 0ul);
if (sem.try_wait())
{
std::cout << "Oops, second instance\n";
}
else
{
sem.post();
// feign hard work for 30s
boost::this_thread::sleep_for(boost::chrono::seconds(30));
if (sem.try_wait())
{
sem.remove("ffed38bd-f0fc-4f79-8838-5301c328268c");
}
}
}
If you start one copy in the back ground, new copies will "refuse" to start ("Oops, second instance") for about 30s.
I have a feeling it might be easier to reverse the logic here. Mmm. Lemme try.
some time passes
Hehe. That was more tricky than I thought.
The thing is, you want to make sure that the lock doesn't remain when your application is interrupted or killed. In the interest of sharing the techniques for portably handling the signals:
#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>
#include <boost/asio.hpp>
#define MAX_PROCESS_INSTANCES 3
boost::interprocess::named_semaphore sem(
boost::interprocess::open_or_create,
"4de7ddfe-2bd5-428f-b74d-080970f980be",
MAX_PROCESS_INSTANCES);
// to handle signals:
boost::asio::io_service service;
boost::asio::signal_set sig(service);
int main()
{
if (sem.try_wait())
{
sig.add(SIGINT);
sig.add(SIGTERM);
sig.add(SIGABRT);
sig.async_wait([](boost::system::error_code,int sig){
std::cerr << "Exiting with signal " << sig << "...\n";
sem.post();
});
boost::thread sig_listener([&] { service.run(); });
boost::this_thread::sleep_for(boost::chrono::seconds(3));
service.post([&] { sig.cancel(); });
sig_listener.join();
}
else
{
std::cout << "More than " << MAX_PROCESS_INSTANCES << " instances not allowed\n";
}
}
There's a lot that could be explained there. Let me know if you're interested.
NOTE It should be quite obvious that if kill -9 is used on your application (forced termination) then all bets are off and you'll have to either remove the Name Semaphore object or explicitly unlock it (post()).
Here's a testrun on my system:
sehe#desktop:/tmp$ (for a in {1..6}; do ./test& done; time wait)
More than 3 instances not allowed
More than 3 instances not allowed
More than 3 instances not allowed
Exiting with signal 0...
Exiting with signal 0...
Exiting with signal 0...
real 0m3.005s
user 0m0.013s
sys 0m0.012s
Here's a simplistic way to implement your own 'semaphore' (since I don't think the standard library or boost have one). This chooses a 'cooperative' approach and workers will wait for each other:
#include <boost/thread.hpp>
#include <boost/phoenix.hpp>
using namespace boost;
using namespace boost::phoenix::arg_names;
void the_work(int id)
{
static int running = 0;
std::cout << "worker " << id << " entered (" << running << " running)\n";
static mutex mx;
static condition_variable cv;
// synchronize here, waiting until we can begin work
{
unique_lock<mutex> lk(mx);
cv.wait(lk, phoenix::cref(running) < 3);
running += 1;
}
std::cout << "worker " << id << " start work\n";
this_thread::sleep_for(chrono::seconds(2));
std::cout << "worker " << id << " done\n";
// signal one other worker, if waiting
{
lock_guard<mutex> lk(mx);
running -= 1;
cv.notify_one();
}
}
int main()
{
thread_group pool;
for (int i = 0; i < 10; ++i)
pool.create_thread(bind(the_work, i));
pool.join_all();
}
Now, I'd say it's probably better to have a dedicated pool of n workers taking their work from a queue in turns:
#include <boost/thread.hpp>
#include <boost/phoenix.hpp>
#include <boost/optional.hpp>
using namespace boost;
using namespace boost::phoenix::arg_names;
class thread_pool
{
private:
mutex mx;
condition_variable cv;
typedef function<void()> job_t;
std::deque<job_t> _queue;
thread_group pool;
boost::atomic_bool shutdown;
static void worker_thread(thread_pool& q)
{
while (auto job = q.dequeue())
(*job)();
}
public:
thread_pool() : shutdown(false) {
for (unsigned i = 0; i < boost::thread::hardware_concurrency(); ++i)
pool.create_thread(bind(worker_thread, ref(*this)));
}
void enqueue(job_t job)
{
lock_guard<mutex> lk(mx);
_queue.push_back(std::move(job));
cv.notify_one();
}
optional<job_t> dequeue()
{
unique_lock<mutex> lk(mx);
namespace phx = boost::phoenix;
cv.wait(lk, phx::ref(shutdown) || !phx::empty(phx::ref(_queue)));
if (_queue.empty())
return none;
auto job = std::move(_queue.front());
_queue.pop_front();
return std::move(job);
}
~thread_pool()
{
shutdown = true;
{
lock_guard<mutex> lk(mx);
cv.notify_all();
}
pool.join_all();
}
};
void the_work(int id)
{
std::cout << "worker " << id << " entered\n";
// no more synchronization; the pool size determines max concurrency
std::cout << "worker " << id << " start work\n";
this_thread::sleep_for(chrono::seconds(2));
std::cout << "worker " << id << " done\n";
}
int main()
{
thread_pool pool; // uses 1 thread per core
for (int i = 0; i < 10; ++i)
pool.enqueue(bind(the_work, i));
}
PS. You can use C++11 lambdas instead boost::phoenix there if you prefer.