I have the following piece of code. I am using c++11 threads to write a simple multi threaded producer consumer problem.
class W
{
public:
explicit W();
void p();
void c();
private:
std::deque<std::uint64_t> q;
std::shared_ptr<std::mutex> m;
std::shared_ptr<std::condition_variable> cvQEmpty;
std::shared_ptr<std::condition_variable> cvQFull;
const std::size_t queue_size;
};
W::W()
: m(std::make_shared<std::mutex>()),
cvQEmpty(std::make_shared<std::condition_variable>()),
cvQFull(std::make_shared<std::condition_variable>()),
queue_size(3)
{
}
void
W::p()
{
while(1)
{
std::unique_lock<std::mutex> lk(*m.get());
if (q.size() >= queue_size)
{
cvQFull->wait(lk, [this] { return q.size() < queue_size; });
}
q.push_back(q.size());
std::cout << "Pushed " << q[q.size() - 1] << std::endl;
lk.unlock();
cvQEmpty->notify_one();
}
}
void
W::c()
{
while (1)
{
std::unique_lock<std::mutex> lk(*m.get());
if (q.empty())
{
cvQEmpty->wait(lk, [this] { return !q.empty(); });
}
while(!q.empty())
{
const std::uint64_t val = q[0];
std::cout << "Output : " << val << std::endl;
q.pop_back();
}
lk.unlock();
cvQFull->notify_one();
}
}
void
foo()
{
W w;
std::thread p(&W::p, w);
std::thread c(&W::c, w);
c.join();
p.join();
}
Both the threads are deadlocked on condition wait.
Could you please tell me where I am going wrong. The program compiles fine without any warnings.
Compiler Used is : g++-5.8
Quite simple. You are copying your w argument to both threads, invoking copy constructor. Those threads end up using two indepenent queues!
Solutions:
Make your queue a shared_ptr like mutex
(better) encompass your argument into std::ref.
(On a side note, explicit W() gives you nothing and is just syntax noise)
Related
I'm trying to solve a dinning philosophers problem using chandy-misra algorithm. More explanation here: https://en.wikipedia.org/wiki/Dining_philosophers_problem
I'm using one mutex to lock the modified variables and another with condition variable to notify when the fork is free to use.
I can't see the reason why all my philosophers are eating at the same time - they are not waiting for forks other at all. It seems like I'm using mutexes wrong.
Philosopher thread:
void philosopher::dine() {
while(!is_initialized); // here threads waits until all other philosophers are initialized
while(!is_stopped) {
eat();
think(); // here just sleeps for a few seconds
}
}
Eat method:
void philosopher::eat() {
left_fork.request(index);
right_fork.request(index);
std::lock(right_fork.get_mutex(), left_fork.get_mutex());
std::lock_guard<std::mutex> l1( right_fork.get_mutex(), std::adopt_lock );
std::lock_guard<std::mutex> l2( left_fork.get_mutex(), std::adopt_lock );
int num = distribution(mt);
std::cout << "Philsopher " << index << " eats for " << num
<< "seconds." << std::endl;
sleep(num);
right_fork.free();
left_fork.free();
}
How fork class looks:
enum fork_state {
CLEAN, DIRTY
};
class fork_t {
int index;
int owner_id;
mutable std::mutex condition_m;
std::mutex owner_m;
std::condition_variable condition;
public:
fork_t(int _index,int _owner_id);
fork_t(const fork_t &f);
void request(int phil_req);
void free();
std::mutex &get_mutex() { return owner_m; }
fork_t& operator=(fork_t const &f);
};
void fork_t::request(int phil_req) {
while (owner_id != phil_req ) {
std::unique_lock<std::mutex> l(condition_m);
if(state == DIRTY) {
std::lock_guard<std::mutex> lock(owner_m);
state = CLEAN;
owner_id = phil_req;
} else {
while(state == CLEAN) {
std::cout<<"Philosopher " << phil_req << " is waiting for"<< index <<std::endl;
condition.wait(l);
}
}
}
}
void fork_t::free() {
state = DIRTY;
condition.notify_one();
}
At the start all forks are given to philosophers with lower id.
I would be grateful for any tips.
I have the following program (made up example!):
#include<thread>
#include<mutex>
#include<iostream>
class MultiClass {
public:
void Run() {
std::thread t1(&MultiClass::Calc, this);
std::thread t2(&MultiClass::Calc, this);
std::thread t3(&MultiClass::Calc, this);
t1.join();
t2.join();
t3.join();
}
private:
void Calc() {
for (int i = 0; i < 10; ++i) {
std::cout << i << std::endl;
}
}
};
int main() {
MultiClass m;
m.Run();
return 0;
}
What I need is to sync the loop iterations the following way and I cant come up with a solution (I've been fiddling for about an hour now using mutexes but cant find THE combination):
t1 and t2 shall do one loop iteration, then t3 shall do one iteration, then again t1 and t2 shall do one, then t3 shall do one.
So you see, I need t1 and t2 to do things simultaneously and after one iteration, t3 shall do one iteration on its own.
Can you point your finger on how I would be able to achieve that? Like I said, ive been trying this with mutexes and cant come up with a solution.
If you really want to do this by hand with the given thread structure, you could use something like this*:
class SyncObj {
mutex mux;
condition_variable cv;
bool completed[2]{ false,false };
public:
void signalCompetionT1T2(int id) {
lock_guard<mutex> ul(mux);
completed[id] = true;
cv.notify_all();
}
void signalCompetionT3() {
lock_guard<mutex> ul(mux);
completed[0] = false;
completed[1] = false;
cv.notify_all();
}
void waitForCompetionT1T2() {
unique_lock<mutex> ul(mux);
cv.wait(ul, [&]() {return completed[0] && completed[1]; });
}
void waitForCompetionT3(int id) {
unique_lock<mutex> ul(mux);
cv.wait(ul, [&]() {return !completed[id]; });
}
};
class MultiClass {
public:
void Run() {
std::thread t1(&MultiClass::Calc1, this);
std::thread t2(&MultiClass::Calc2, this);
std::thread t3(&MultiClass::Calc3, this);
t1.join();
t2.join();
t3.join();
}
private:
SyncObj obj;
void Calc1() {
for (int i = 0; i < 10; ++i) {
obj.waitForCompetionT3(0);
std::cout << "T1:" << i << std::endl;
obj.signalCompetionT1T2(0);
}
}
void Calc2() {
for (int i = 0; i < 10; ++i) {
obj.waitForCompetionT3(1);
std::cout << "T2:" << i << std::endl;
obj.signalCompetionT1T2(1);
}
}
void Calc3() {
for (int i = 0; i < 10; ++i) {
obj.waitForCompetionT1T2();
std::cout << "T3:" << i << std::endl;
obj.signalCompetionT3();
}
}
};
However, this is only a reasonable approach, if each iteration is computational expensive, such that you can ignore the synchronization overhead. If that is not the case you should probably better have a look at a proper parallel programming library like intel's tbb or microsofts ppl.
*)NOTE: This code is untested and unoptimized. I just wrote it to show what the general structure could look like
Use two condition variables, here is a sketch..
thread 1 & 2 wait on condition variable segment_1:
std::condition_variable segment_1;
thread 3 waits on condition variable segment_2;
std::condition_variable segment_2;
threads 1 & 2 should wait() on segment_1, and thread 3 should wait() on segment_2. To kick off threads 1 & 2, call notify_all() on segment_1, and once they complete, call notify_one() on segment_2 to kick off thread 3. You may want to use some controlling thread to control the sequence unless you can chain (i.e. once 1 & 2 complete, the last one to complete calls notify for thread 3 and so on..)
This is not perfect (see lost wakeups)
I am trying to do the following in a multi-threaded environment (it does not implement the solution correctly at the moment but you can get the intent)
struct object {
object() : numReaders(0) {}
void tryRead() {
numReaders++;
if(!isDestroyed) {
// do something
}
numReaders--;
}
void destroy() {
if(numReaders == 0) {
// <- if there is a reader here we have a problem
isDestroyed = 1;
} else {
// wait until all readers are done and destroy
}
}
std::atomic<int> numReaders;
std::atomic<int> isDestroyed;
};
this sure looks like a std::shared_mutex or a reader/writer lock problem. Is this particular problem solvable in the context of c++11 in a clean and short way (without copying implementations from c++14 or smth) and without using third party libraries?
A solution attempt:
OK, so I wrote something like this (destroy in the case above is a writer, and try read is a reader). There are 3 solutions, the 2nd and 3rd one trying to create less boiler plate code and 3rd also tried to be exception safe (if an exception is thrown no deadlock occurs). However, running 2nd solution works just fine, the 3rd one gets a deadlock, although seemingly 3rd is just a slighly modified 2nd, relying on the fact that C++ would call destructor at the end of the scope.
#include <mutex>
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
#include <condition_variable>
//--------------------------------------------------------------------------
// Reader/writer solution 1
//--------------------------------------------------------------------------
class Doer {
std::mutex requestLock_;
std::atomic<int> numRequests_;
std::condition_variable hasNoRequests_;
public:
Doer()
: numRequests_(0)
{
}
void reader()
{
{
std::lock_guard<std::mutex> guard(requestLock_);
numRequests_++;
}
std::cout << "read" << std::endl;
{
std::lock_guard<std::mutex> guard(requestLock_);
numRequests_--;
// notify a potential writer that read is over
}
hasNoRequests_.notify_one();
}
void writer()
{
std::unique_lock<std::mutex> guard(requestLock_, std::defer_lock_t());
hasNoRequests_.wait(guard, [this]() {return numRequests_ == 0; });
std::cout << std::endl << "write" << std::endl;
guard.unlock();
// wake up only 1 writer
hasNoRequests_.notify_one();
}
};
//--------------------------------------------------------------------------
// Reader/writer solution 2
//--------------------------------------------------------------------------
class SmartDoer {
std::mutex requestLock_;
std::atomic<int> numRequests_;
std::condition_variable hasNoRequests_;
std::function<void()> startRead_;
std::function<void()> finishRead_;
std::function<std::unique_lock<std::mutex>()> startWrite_;
std::function<void(std::unique_lock<std::mutex>)> finishWrite_;
public:
SmartDoer() : numRequests_(0)
{
startRead_ = [this]() {
std::lock_guard<std::mutex> guard(requestLock_);
std::cout << "start read" << std::endl;
numRequests_++;
};
finishRead_ = [this]() {
{
std::lock_guard<std::mutex> guard(requestLock_);
numRequests_--;
std::cout << "finish read" << std::endl;
}
// notify a potential writer that read is over
hasNoRequests_.notify_one();
};
startWrite_ = [this]() {
std::unique_lock<std::mutex> guard(requestLock_, std::defer_lock_t());
hasNoRequests_.wait(guard, [this]() {return numRequests_ == 0; });
std::cout << "start write" << std::endl;
return guard;
};
finishWrite_ = [this](std::unique_lock<std::mutex>&& guard) {
std::cout << "finish write" << std::endl;
guard.unlock();
// wake up only 1 writer
hasNoRequests_.notify_one();
};
}
void reader()
{
startRead_();
std::cout << "read" << std::endl;
finishRead_();
}
void writer()
{
auto result = startWrite_();
std::cout << std::endl << "write" << std::endl;
finishWrite_(std::move(result));
}
};
//--------------------------------------------------------------------------
// Reader/writer solution 3
//--------------------------------------------------------------------------
template <typename Start, typename Finish>
class ScopeGuard {
public:
ScopeGuard(Start& start, Finish& finish)
: start_(start)
, finish_(finish)
, engaged_(true)
{
auto result_ = start_();
}
~ScopeGuard()
{
if (engaged_) {
std::bind(finish_, std::move(result_));
}
}
void release()
{
engaged_ = false;
}
private:
std::result_of<Start()> result_;
Start start_;
Finish finish_;
bool engaged_;
};
template <typename Start, typename Finish>
class VoidScopeGuard {
public:
VoidScopeGuard(Start& start, Finish& finish)
: start_(start)
, finish_(finish)
, engaged_(true)
{
start_();
}
~VoidScopeGuard()
{
if (engaged_) {
finish_();
}
}
void release()
{
engaged_ = false;
}
private:
Start start_;
Finish finish_;
bool engaged_;
};
class TheSmartestDoer {
std::mutex requestLock_;
std::atomic<int> numRequests_;
std::condition_variable hasNoRequests_;
std::function<void()> startRead_;
std::function<void()> finishRead_;
std::function<std::unique_lock<std::mutex>()> startWrite_;
std::function<void(std::unique_lock<std::mutex>&&)> finishWrite_;
public:
TheSmartestDoer() : numRequests_(0)
{
startRead_ = [this]() {
std::lock_guard<std::mutex> guard(requestLock_);
std::cout << "start read" << std::endl;
numRequests_++;
};
finishRead_ = [this]() {
{
std::lock_guard<std::mutex> guard(requestLock_);
numRequests_--;
std::cout << "finish read" << std::endl;
}
// notify a potential writer that read is over
hasNoRequests_.notify_one();
};
startWrite_ = [this]() {
std::unique_lock<std::mutex> guard(requestLock_, std::defer_lock_t());
hasNoRequests_.wait(guard, [this]() {return numRequests_ == 0; });
std::cout << "start write" << std::endl;
return guard;
};
finishWrite_ = [this](std::unique_lock<std::mutex>&& guard) {
std::cout << "finish write" << std::endl;
guard.unlock();
// wake up only 1 writer
hasNoRequests_.notify_one();
};
}
void reader()
{
VoidScopeGuard<decltype(startRead_), decltype(finishRead_)> guard(startRead_, finishRead_);
std::cout << "read" << std::endl;
}
void writer()
{
ScopeGuard<decltype(startWrite_), decltype(finishWrite_)> guard(startWrite_, finishWrite_);
std::cout << std::endl << "write" << std::endl;
}
};
int main()
{
TheSmartestDoer doit;
std::vector<std::thread> write(10000);
for (int i = 0; i < write.size(); i++) {
write[i] = std::thread(&TheSmartestDoer::writer, &doit);
}
std::vector<std::thread> read(10000);
for (int i = 0; i < read.size(); i++) {
read[i] = std::thread(&TheSmartestDoer::reader, &doit);
}
for (int i = 0; i < write.size(); i++) {
write[i].join();
}
for (int i = 0; i < read.size(); i++) {
read[i].join();
}
return 0;
}
The simplest way is to use weak reference paradigm, that is object's reference which do not prevent object from being deleted. c++11 has std::weak_ptr<> class for that paradigm:
#include <memory>
class Object {...};
std::weak_ptr<Object> wptr; // Weak reference for use in (reader) threads
int main()
{
std::shared_ptr<Object> sptr; // Strong reference, determine lifetime of the object
//...
sptr = std::make_shared<Object>(...);// Create object and store reference to it
wptr = sptr;
//...
sptr.reset(); // Mark object to be destroyed. Since that moment weak reference is treated as expired, no one can access object via it.
}
void reader()
{
std::shared_ptr<Object> tptr = wptr.lock(); // Temporary create strong reference from the weak one
if(tptr)
{
// Do something with object. It won't be deleted while 'tptr' is alive
}
}
Both std::shared_ptr and std::weak_ptr are already prepared for multithreaded usage.
I've started to learn concurrency(C++11) reading the book C++ Concurrency in Action. How to test a thread-safe stack class (Example was taken from C++ concurrency in action listing 3.5). I would like to have differents implementations of producer/consumer functions that let me test all its functions.
#include <exception>
#include <memory>
#include <mutex>
#include <stack>
struct empty_stack: std::exception
{
const char* what() const throw();
};
template<typename T>
class threadsafe_stack
{
private:
std::stack<T> data;
mutable std::mutex m;
public:
threadsafe_stack() {}
threadsafe_stack(const threadsafe_stack& other)
{
std::lock_guard<std::mutex> lock(other.m);
data=other.data;
}
threadsafe_stack& operator = (const threadsafe_stack&) = delete;
void push(T new_value)
{
std::lock_guard<std::mutex> lock(m);
data.push(new_value);
}
std::shared_ptr<T> pop()
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
data.pop();
return res;
}
void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if (data.empty()) throw empty_stack();
value = data.top();
data.pop();
}
bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
};
int main()
{
//test class
return 0;
}
You simply need to:
Create a stack from your main function
Start a thread that will fill the stack (pass the stack object pointer as parameter to the thread and make the thread execute a for loop filling the stack by calling push all the time)
Then, while this thread runs, empty the stack from another loop of your main program
You can also declare the stack as a global variable if you simply want to do a quick test and don't know how to pass objects to the thread upon creation.
If you need clean exit, add an atomic (edited, I first recommended volatile) bool passed to the thread to tell it you're done and ask it to stop its loop. Then use join to wait for the thread to exit.
A minimal testdriver for your structure could look like this:
struct Msg {
size_t a;size_t b;size_t c;size_t d;
};
bool isCorrupted(const Msg& m) {
return !(m.a == m.b && m.b == m.c && m.c == m.d);
}
int main()
{
threadsafe_stack<Msg> stack;
auto prod = std::async(std::launch::async, [&]() {
for (size_t i = 0; i < 1000000; ++i){
Msg m = { i, i, i, i };
stack.push(m);
//std::this_thread::sleep_for(std::chrono::microseconds(1));
if (i % 1000 == 0) {
std::cout << "stack.push called " << i << " times " << std::endl;
}
}
});
auto cons = std::async(std::launch::async, [&]() {
for (size_t i = 0; i < 1000000; ++i){
try {
Msg m;
stack.pop(m);
if (isCorrupted(m)) {
std::cout << i <<" ERROR: MESSAGE WAS CORRUPED:" << m.a << "-" << m.b << "-" << m.c << "-" << m.d << std::endl;
}
if (i % 1000 == 0) {
std::cout << "stack.pop called " << i << " times " << std::endl;
}
}
catch (empty_stack e) {
std::cout << i << " Stack was empty!" << std::endl;
}
}
});
prod.wait();
cons.wait();
return 0;
}
Note, that this doesn't test all different functions, nor for all possible race conditions, so you'd have to exend it.
Two recommendations regarding your class design:
1) I wouldn't throw an exception when the stack is empty, as this is a very common case in an asynchronous scenario. Rather make the consumer thread wait (see condition variables for this) or return a false or nullptr respectively.
2) Use std::unique_ptr instead of std::shared_ptr<T> in your pop() function as it is more efficient and you don't share anything here anyway.
I found a good implementation of boost based thread pool which is an improvement over this and this . it is very easy to understand and test. It looks like this:
#include <boost/thread/thread.hpp>
#include <boost/asio.hpp>
// the actual thread pool
struct ThreadPool {
ThreadPool(std::size_t);
template<class F>
void enqueue(F f);
~ThreadPool();
// the io_service we are wrapping
boost::asio::io_service io_service;
// dont let io_service stop
boost::shared_ptr<boost::asio::io_service::work> work;
//the threads
boost::thread_group threads;
};
// the constructor just launches some amount of workers
ThreadPool::ThreadPool(size_t nThreads)
:io_service()
,work(new boost::asio::io_service::work(io_service))
{
for ( std::size_t i = 0; i < nThreads; ++i ) {
threads.create_thread(boost::bind(&boost::asio::io_service::run, &io_service));
}
}
// add new work item to the pool
template<class F>
void ThreadPool::enqueue(F f) {
io_service.post(f);
}
// the destructor joins all threads
ThreadPool::~ThreadPool() {
work.reset();
io_service.run();
}
//tester:
void f(int i)
{
std::cout << "hello " << i << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(300));
std::cout << "world " << i << std::endl;
}
//it can be tested via:
int main() {
// create a thread pool of 4 worker threads
ThreadPool pool(4);
// queue a bunch of "work items"
for( int i = 0; i < 8; ++i ) {
std::cout << "task " << i << " created" << std::endl;
pool.enqueue(boost::bind(&f,i));
}
}
g++ ThreadPool-4.cpp -lboost_system -lboost_thread
Now the question:
I need to know how I can modify the implementation to be able to use this thread pool batch by batch- only when the first set of my work is fully completed by the thread pool, I need to supply the second set and so on. I tried to play with .run() and .reset() (found in the destructor) between the batch jobs but no luck:
//adding methods to the tread pool :
//reset the asio work and thread
void ThreadPool::reset(size_t nThreads){
work.reset(new boost::asio::io_service::work(io_service));
for ( std::size_t i = 0; i < nThreads; ++i ) {
threads.create_thread(boost::bind(&boost::asio::io_service::run, &io_service));
}
std::cout << "group size : " << threads.size() << std::endl;
}
//join, and even , interrupt
void ThreadPool::joinAll(){
threads.join_all();
threads.interrupt_all();
}
//tester
int main() {
// create a thread pool of 4 worker threads
ThreadPool pool(4);
// queue a bunch of "work items"
for( int i = 0; i < 20; ++i ) {
std::cout << "task " << i << " created" << std::endl;
pool.enqueue(boost::bind(&f,i));
}
//here i play with the asio work , io_service and and the thread group
pool.work.reset();
pool.io_service.run();
std::cout << "after run" << std::endl;
pool.joinAll();
std::cout << "after join all" << std::endl;
pool.reset(4);
std::cout << "new thread group size: " << pool.threads.size() << std::endl;///btw: new threa group size is 8. I expected 4!
// second batch... never completes
for( int i = 20; i < 30; ++i ) {
pool.enqueue(boost::bind(&f,i));
}
}
The second batch doesn't complete. I will appreciate if you help me fix this.
thank you
UPDATE- Solution:
based on a solution by Nik, I developed a solution using condition variable. Just add the following code to the original class:
// add new work item to the pool
template<class F>
void ThreadPool::enqueue(F f) {
{
boost::unique_lock<boost::mutex> lock(mutex_);
nTasks ++;
}
//forwarding the job to wrapper()
void (ThreadPool::*ff)(boost::tuple<F>) = &ThreadPool::wrapper<F>;
io_service.post(boost::bind(ff, this, boost::make_tuple(f))); //using a tuple seems to be the only practical way. it is mentioned in boost examples.
}
//run+notfiy
template<class F>
void ThreadPool::wrapper(boost::tuple<F> f) {
boost::get<0>(f)();//this is the task (function and its argument) that has to be executed by a thread
{
boost::unique_lock<boost::mutex> lock(mutex_);
nTasks --;
cond.notify_one();
}
}
void ThreadPool::wait(){
boost::unique_lock<boost::mutex> lock(mutex_);
while(nTasks){
cond.wait(lock);
}
}
Now you may call wait() method between batches of work.
one problem however:
Even after the last batch, I have to call pool.wait() because the thread pool's scope will end after that and thread pool's destructor will be invoked. During destruction, some of the jobs are done and it will be the time to call the .notify(). As the Threadpool::mutex during destruction is invalidated, exceptions occur during locking. your suggestion will be appreciated.
A condition variable could be used to achieve desired result.
Implement a function responsible for calling enqueue the tasks and wait on a condition variable.
Condition variable is notified when all tasks assigned to the pool are complete.
Every thread checks if the jobs are complete or not. Once all the jobs are complete condition variable is notified.
//An example of what you could try, this just an hint for what could be explored.
void jobScheduler()
{
int jobs = numberOfJobs; //this could vary and can be made shared memory
// queue a bunch of "work items"
for( int i = 0; i < jobs; ++i )
{
std::cout << "task " << i << " created" << std::endl;
pool.enqueue(boost::bind(&f,i));
}
//wait on a condition variable
boost::mutex::scoped_lock lock(the_mutex);
conditionVariable.wait(lock); //Have this varibale notified from any thread which realizes that all jobs are complete.
}
Solution 2
I have a new working solution, with some assumption about syntax of functions being called back, but that could be changed as per requirement.
Continuing on the lines of above I use condition variable for managing my tasks but with a difference.
Create a queue of jobs.
A Manager which waits for new JOBS in the queue.
Once a job is received a notification is sent to waiting manager about the same.
Worker maintains a handle to Manager. When all the tasks assigned are complete Manger is informed.
Manager on getting a call for end, stops waiting for new JOBS in queue and exits.
#include <iostream>
#include <queue>
#include <boost/thread/thread.hpp>
#include <boost/asio.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/function.hpp>
///JOB Queue hold all jobs required to be executed
template<typename Job>
class JobQueue
{
private:
std::queue<Job> _queue;
mutable boost::mutex _mutex;
boost::condition_variable _conditionVariable;
public:
void push(Job const& job)
{
boost::mutex::scoped_lock lock(_mutex);
_queue.push(job);
lock.unlock();
_conditionVariable.notify_one();
}
bool empty() const
{
boost::mutex::scoped_lock lock(_mutex);
return _queue.empty();
}
bool tryPop(Job& poppedValue)
{
boost::mutex::scoped_lock lock(_mutex);
if(_queue.empty())
{
return false;
}
poppedValue = _queue.front();
_queue.pop();
return true;
}
void waitAndPop(Job& poppedValue)
{
boost::mutex::scoped_lock lock(_mutex);
while(_queue.empty())
{
_conditionVariable.wait(lock);
}
poppedValue = _queue.front();
_queue.pop();
}
};
///Thread pool for posting jobs to io service
class ThreadPool
{
public :
ThreadPool( int noOfThreads = 1) ;
~ThreadPool() ;
template< class func >
void post( func f ) ;
boost::asio::io_service &getIoService() ;
private :
boost::asio::io_service _ioService;
boost::asio::io_service::work _work ;
boost::thread_group _threads;
};
inline ThreadPool::ThreadPool( int noOfThreads )
: _work( _ioService )
{
for(int i = 0; i < noOfThreads ; ++i) // 4
_threads.create_thread(boost::bind(&boost::asio::io_service::run, &_ioService));
}
inline ThreadPool::~ThreadPool()
{
_ioService.stop() ;
_threads.join_all() ;
}
inline boost::asio::io_service &ThreadPool::getIoService()
{
return _ioService ;
}
template< class func >
void ThreadPool::post( func f )
{
_ioService.post( f ) ;
}
template<typename T>
class Manager;
///Worker doing some work.
template<typename T>
class Worker{
T _data;
int _taskList;
boost::mutex _mutex;
Manager<T>* _hndl;
public:
Worker(T data, int task, Manager<T>* hndle):
_data(data),
_taskList(task),
_hndl(hndle)
{
}
bool job()
{
boost::mutex::scoped_lock lock(_mutex);
std::cout<<"...Men at work..."<<++_data<<std::endl;
--_taskList;
if(taskDone())
_hndl->end();
}
bool taskDone()
{
std::cout<<"Tasks "<<_taskList<<std::endl<<std::endl;
if(_taskList == 0)
{
std::cout<<"Tasks done "<<std::endl;
return true;
}
else false;
}
};
///Job handler waits for new jobs and
///execute them as when a new job is received using Thread Pool.
//Once all jobs are done hndler exits.
template<typename T>
class Manager{
public:
typedef boost::function< bool (Worker<T>*)> Func;
Manager(int threadCount):
_threadCount(threadCount),
_isWorkCompleted(false)
{
_pool = new ThreadPool(_threadCount);
boost::thread jobRunner(&Manager::execute, this);
}
void add(Func f, Worker<T>* instance)
{
Job job(instance, f);
_jobQueue.push(job);
}
void end()
{
boost::mutex::scoped_lock lock(_mutex);
_isWorkCompleted = true;
//send a dummy job
add( NULL, NULL);
}
void workComplete()
{
std::cout<<"Job well done."<<std::endl;
}
bool isWorkDone()
{
boost::mutex::scoped_lock lock(_mutex);
if(_isWorkCompleted)
return true;
return false;
}
void execute()
{
Job job;
while(!isWorkDone())
{
_jobQueue.waitAndPop(job);
Func f = boost::get<1>(job);
Worker<T>* ptr = boost::get<0>(job);
if(f)
{
_pool->post(boost::bind(f, ptr));
}
else
break;
}
std::cout<<"Complete"<<std::endl;
}
private:
ThreadPool *_pool;
int _threadCount;
typedef boost::tuple<Worker<T>*, Func > Job;
JobQueue<Job> _jobQueue;
bool _isWorkCompleted;
boost::mutex _mutex;
};
typedef boost::function< bool (Worker<int>*)> IntFunc;
typedef boost::function< bool (Worker<char>*)> CharFunc;
int main()
{
boost::asio::io_service ioService;
Manager<int> jobHndl(2);
Worker<int> wrk1(0,4, &jobHndl);
IntFunc f= &Worker<int>::job;
jobHndl.add(f, &wrk1);
jobHndl.add(f, &wrk1);
jobHndl.add(f, &wrk1);
jobHndl.add(f, &wrk1);
Manager<char> jobHndl2(2);
Worker<char> wrk2(0,'a', &jobHndl2);
CharFunc f2= &Worker<char>::job;
jobHndl2.add(f2, &wrk2);
jobHndl2.add(f2, &wrk2);
jobHndl2.add(f2, &wrk2);
jobHndl2.add(f2, &wrk2);
ioService.run();
while(1){}
return 0;
}
The third solution is the best (easiest IMHO), the one from the asio father;
You have to understand that you will stay blocked on "Threads.join_all()" statement while there is still a thread alive. Then you can call again with other work to do.
May be an alternative is to use taskqueue "A task queue that uses a thread pool to complete tasks in parallel", you fill up the queue with your works, it ensures that there will be no more than 'x' tasks working in parallel.
Sample is easy to understand.
May be you need to add that member function to TaskQueue class in order to solve your "pool.wait()" issue:
void WaitForEmpty(){
while( NumPendingTasks() || threads_.size() ){
boost::wait_for_any(futures_.begin(), futures_.end());
}
}
Enjoy !