thread safe object state manipulation in c++11 - c++

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.

Related

Thread safe ExpiringDeque data structure in C++

I am trying to create a data structure, ExpiringDeque. It should be somewhat similar to std::deque. Let's say I need only push_back(), size() and pop_front(). The data structure needs to automatically expire up to N first elements every T seconds.
This data structure needs to manage its own queue and expiration thread internally.
How do I write it in a thread safe way? This is an example that I came up with, does this seem reasonable? What am I missing?
#include <algorithm>
#include <atomic>
#include <cassert>
#include <deque>
#include <mutex>
#include <thread>
#include <unistd.h>
#include <iostream>
template <typename T>
class ExpiringDeque {
public:
ExpiringDeque(int n, int t) : numElements_(n), interval_(t), running_(true), items_({}) {
expiringThread_ = std::thread{[&] () {
using namespace std::chrono_literals;
int waitCounter = 0;
while (true) {
if (!running_) {
return;
}
std::this_thread::sleep_for(1s);
if (waitCounter++ < interval_) {
continue;
}
std::lock_guard<std::mutex> guard(mutex_);
waitCounter = 0;
int numToErase = std::min(numElements_, static_cast<int>(items_.size()));
std::cout << "Erasing " << numToErase << " elements\n";
items_.erase(items_.begin(), items_.begin() + numToErase);
}
}};
}
~ExpiringDeque() {
running_ = false;
expiringThread_.join();
}
T pop_front() {
if (items_.size() == 0) {
throw std::out_of_range("Empty deque");
}
std::lock_guard<std::mutex> guard(mutex_);
T item = items_.front();
items_.pop_front();
return item;
}
int size() {
std::lock_guard<std::mutex> guard(mutex_);
return items_.size();
}
void push_back(T item) {
std::lock_guard<std::mutex> guard(mutex_);
items_.push_back(item);
}
private:
int numElements_;
int interval_;
std::atomic<bool> running_;
std::thread expiringThread_;
std::mutex mutex_;
std::deque<T> items_;
};
int main() {
ExpiringDeque<int> ed(10, 3);
ed.push_back(1);
ed.push_back(2);
ed.push_back(3);
assert(ed.size() == 3);
assert(ed.pop_front() == 1);
assert(ed.size() == 2);
// wait for expiration
sleep(5);
assert(ed.size() == 0);
ed.push_back(10);
assert(ed.size() == 1);
assert(ed.pop_front() == 10);
return 0;
}
You can avoid an unnecessary wait in the destructor of ExpiringDeque by using a condition variable. I would also use std::condition_variable::wait_for with a predicate to check the running_ flag. This will ensure that you either wait for a timeout or a notification, whichever is earlier. You avoid using waitCounter and continue this way.
Another thing you should do is lock the mutex before checking the size of your deque in pop_front(), otherwise it's not thread safe.
Here's an updated version of your code:
template <typename T>
class ExpiringDeque {
public:
ExpiringDeque(int n, int t) : numElements_(n), interval_(t), running_(true), items_({}), cv_() {
expiringThread_ = std::thread{ [&]() {
using namespace std::chrono_literals;
while (true) {
//Wait for timeout or notification
std::unique_lock<std::mutex> lk(mutex_);
cv_.wait_for(lk, interval_ * 1s, [&] { return !running_; });
if (!running_)
return;
//Mutex is locked already - no need to lock again
int numToErase = std::min(numElements_, static_cast<int>(items_.size()));
std::cout << "Erasing " << numToErase << " elements\n";
items_.erase(items_.begin(), items_.begin() + numToErase);
}
} };
}
~ExpiringDeque() {
//Set flag and notify worker thread
{
std::lock_guard<std::mutex> lk(mutex_);
running_ = false;
}
cv_.notify_one();
expiringThread_.join();
}
T pop_front() {
std::lock_guard<std::mutex> guard(mutex_);
if (items_.size() == 0) {
throw std::out_of_range("Empty deque");
}
T item = items_.front();
items_.pop_front();
return item;
}
...
private:
int numElements_;
int interval_;
bool running_;
std::thread expiringThread_;
std::mutex mutex_;
std::deque<T> items_;
std::condition_variable cv_;
};
You can make the running_ flag a normal bool since the std::condition_variable::wait_for atomically checks for the timeout or notification.

Simple worker thread in C++ class

Assume that there is a class which contains some data and calculates some results given queries, and the queries take a relatively large amount of time.
An example class (everything dummy) is:
#include <vector>
#include <numeric>
#include <thread>
struct do_some_work
{
do_some_work(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
{}
void update_query(size_t x) {
if (x < _data.size()) {
_current_query = x;
recalculate_result();
}
}
int get_result() const {
return _last_calculated_result;
}
private:
void recalculate_result() {
//dummy some work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
_last_calculated_result = std::accumulate(_data.cbegin(), _data.cbegin() + _current_query, 0);
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
};
and this can be used in the main code like:
#include <algorithm>
int main()
{
//make some dummy data
std::vector<int> test_data(20, 0);
std::iota(test_data.begin(), test_data.end(), 0);
{
do_some_work work(test_data);
for (size_t i = 0; i < test_data.size(); ++i) {
work.update_query(i);
std::cout << "result = {" << i << "," << work.get_result() << "}" << std::endl;
}
}
}
The above will wait in the main function a lot.
Now, assuming we want to run this querying in a tight loop (say GUI) and only care about about getting a "recent" result quickly when we query.
So, we want to move the work to a separate thread which calculates the results, and updates it, and when we get result, we get the last calculated one. That is, we want to change do_some_work class to do its work on a thread, with minimal changes (essentially find a pattern of changes that can be applied to (mostly) any class of this type).
My stab at this is the following:
#include <vector>
#include <numeric>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
struct do_lots_of_work
{
do_lots_of_work(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _worker()
, _data_mtx()
, _result_mtx()
, _cv()
, _do_exit(false)
, _work_available(false)
{
start_worker();
}
void update_query(size_t x) {
{
if (x < _data.size()) {
std::lock_guard<std::mutex> lck(_data_mtx);
_current_query = x;
_work_available = true;
_cv.notify_one();
}
}
}
int get_result() const {
std::lock_guard<std::mutex> lck(_result_mtx);
return _last_calculated_result;
}
~do_lots_of_work() {
stop_worker();
}
private:
void start_worker() {
if (!_worker.joinable()) {
std::cout << "starting worker..." << std::endl;
_worker = std::thread(&do_lots_of_work::worker_loop, this);
}
}
void stop_worker() {
std::cout << "worker stopping..." << std::endl;
if (_worker.joinable()) {
std::unique_lock<std::mutex> lck(_data_mtx);
_do_exit = true;
lck.unlock();
_cv.notify_one();
_worker.join();
}
std::cout << "worker stopped" << std::endl;
}
void worker_loop() {
std::cout << "worker started" << std::endl;
while (true) {
std::unique_lock<std::mutex> lck(_data_mtx);
_cv.wait(lck, [this]() {return _work_available || _do_exit; });
if (_do_exit) { break; }
if (_work_available) {
_work_available = false;
int query = _current_query; //take local copy
lck.unlock(); //unlock before doing lots of work.
recalculate_result(query);
}
}
}
void recalculate_result(int query) {
//dummy lots of work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int const result = std::accumulate(_data.cbegin(), _data.cbegin() + query, 0);
set_result(result);
}
void set_result(int result) {
std::lock_guard<std::mutex> lck(_result_mtx);
_last_calculated_result = result;
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
std::thread _worker;
mutable std::mutex _data_mtx;
mutable std::mutex _result_mtx;
std::condition_variable _cv;
bool _do_exit;
bool _work_available;
};
and the usage is (example):
#include <algorithm>
int main()
{
//make some dummy data
std::vector<int> test_data(20, 0);
std::iota(test_data.begin(), test_data.end(), 0);
{
do_lots_of_work work(test_data);
for (size_t i = 0; i < test_data.size(); ++i) {
work.update_query(i);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "result = {" << i << "," << work.get_result() << "}" << std::endl;
}
}
}
This seems to work, giving the last result, not stopping the main function etc.
But, this looks a LOT of changes are required to add a worker thread to a simple class like do_some_work. Items like two mutexes (one for the worker/main interaction data, and one for the result), one condition_variable, one more-work-available flag and one do-exit flag, that is quite a bit. I guess we don't want an async kind of mechanism because we don't want to potentially launch a new thread every time.
Now, I am not sure if there is a MUCH simpler pattern to make this kind of change, but it feels like there should be. A kind of pattern that can be used to off-load work to a thread.
So finally, my question is, can do_some_work be converted into do_lots_of_work in a much simpler way than the implementation above?
Edit (Solution 1) ThreadPool based:
Using a threadpool, the worker loop can be skipped, we need two mutexes, for result and query. Lock in updating query, Lock in getting result, Both lock in recalculate (take a local copy of a query, and write to result).
Note: Also, when pushing work on the queue, as we do not care about the older results, we can clear the work queue.
Example implementation (using the CTPL threadpool)
#include "CTPL\ctpl_stl.h"
#include <vector>
#include <mutex>
struct do_lots_of_work_with_threadpool
{
do_lots_of_work_with_threadpool(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _pool(1)
, _result_mtx()
, _query_mtx()
{
}
void update_query(size_t x) {
if (x < _data.size()) {
std::lock_guard<std::mutex> lck(_query_mtx);
_current_query = x;
}
_pool.clear_queue(); //clear as we don't want to calculate any out-date results.
_pool.push([this](int id) { recalculate_result(); });
}
int get_result() const {
std::lock_guard<std::mutex> lck(_result_mtx);
return _last_calculated_result;
}
private:
void recalculate_result() {
//dummy some work here
size_t query;
{
std::lock_guard<std::mutex> lck(_query_mtx);
query = _current_query;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int result = std::accumulate(_data.cbegin(), _data.cbegin() + query, 0);
{
std::lock_guard<std::mutex> lck(_result_mtx);
_last_calculated_result = result;
}
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
ctpl::thread_pool _pool;
mutable std::mutex _result_mtx;
mutable std::mutex _query_mtx;
};
Edit (Solution 2) With ThreadPool and Atomic:
This solution changes the shared variables to atomic, and so we do not need any mutexes and do not have to consider taking/releasing locks etc. This is much simpler and very close to the original class (of course assumes a threadpool type exists somewhere as it is not part of the standard).
#include "CTPL\ctpl_stl.h"
#include <vector>
#include <mutex>
#include <atomic>
struct do_lots_of_work_with_threadpool_and_atomics
{
do_lots_of_work_with_threadpool_and_atomics(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _pool(1)
{
}
void update_query(size_t x) {
if (x < _data.size()) {
_current_query.store(x);
}
_pool.clear_queue(); //clear as we don't want to calculate any out-date results.
_pool.push([this](int id) { recalculate_result(); });
}
int get_result() const {
return _last_calculated_result.load();
}
private:
void recalculate_result() {
//dummy some work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
_last_calculated_result.store(std::accumulate(_data.cbegin(), _data.cbegin() + _current_query.load(), 0));
}
std::vector<int> const _data;
std::atomic<size_t> _current_query;
std::atomic<int> _last_calculated_result;
ctpl::thread_pool _pool;
};

std::thread throwing "resource dead lock would occur"

I have a list of objects, each object has member variables which are calculated by an "update" function. I want to update the objects in parallel, that is I want to create a thread for each object to execute it's update function.
Is this a reasonable thing to do? Any reasons why this may not be a good idea?
Below is a program which attempts to do what I described, this is a complete program so you should be able to run it (I'm using VS2015). The goal is to update each object in parallel. The problem is that once the update function completes, the thread throws an "resource dead lock would occur" exception and aborts.
Where am I going wrong?
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <thread>
#include <mutex>
#include <chrono>
class Object
{
public:
Object(int sleepTime, unsigned int id)
: m_pSleepTime(sleepTime), m_pId(id), m_pValue(0) {}
void update()
{
if (!isLocked()) // if an object is not locked
{
// create a thread to perform it's update
m_pThread.reset(new std::thread(&Object::_update, this));
}
}
unsigned int getId()
{
return m_pId;
}
unsigned int getValue()
{
return m_pValue;
}
bool isLocked()
{
bool mutexStatus = m_pMutex.try_lock();
if (mutexStatus) // if mutex is locked successfully (meaning it was unlocked)
{
m_pMutex.unlock();
return false;
}
else // if mutex is locked
{
return true;
}
}
private:
// private update function which actually does work
void _update()
{
m_pMutex.lock();
{
std::cout << "thread " << m_pId << " sleeping for " << m_pSleepTime << std::endl;
std::chrono::milliseconds duration(m_pSleepTime);
std::this_thread::sleep_for(duration);
m_pValue = m_pId * 10;
}
m_pMutex.unlock();
try
{
m_pThread->join();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl; // throws "resource dead lock would occur"
}
}
unsigned int m_pSleepTime;
unsigned int m_pId;
unsigned int m_pValue;
std::mutex m_pMutex;
std::shared_ptr<std::thread> m_pThread; // store reference to thread so it doesn't go out of scope when update() returns
};
typedef std::shared_ptr<Object> ObjectPtr;
class ObjectManager
{
public:
ObjectManager()
: m_pNumObjects(0){}
void updateObjects()
{
for (int i = 0; i < m_pNumObjects; ++i)
{
m_pObjects[i]->update();
}
}
void removeObjectByIndex(int index)
{
m_pObjects.erase(m_pObjects.begin() + index);
}
void addObject(ObjectPtr objPtr)
{
m_pObjects.push_back(objPtr);
m_pNumObjects++;
}
ObjectPtr getObjectByIndex(unsigned int index)
{
return m_pObjects[index];
}
private:
std::vector<ObjectPtr> m_pObjects;
int m_pNumObjects;
};
void main()
{
int numObjects = 2;
// Generate sleep time for each object
std::vector<int> objectSleepTimes;
objectSleepTimes.reserve(numObjects);
for (int i = 0; i < numObjects; ++i)
objectSleepTimes.push_back(rand());
ObjectManager mgr;
// Create some objects
for (int i = 0; i < numObjects; ++i)
mgr.addObject(std::make_shared<Object>(objectSleepTimes[i], i));
// Print expected object completion order
// Sort from smallest to largest
std::sort(objectSleepTimes.begin(), objectSleepTimes.end());
for (int i = 0; i < numObjects; ++i)
std::cout << objectSleepTimes[i] << ", ";
std::cout << std::endl;
// Update objects
mgr.updateObjects();
int numCompleted = 0; // number of objects which finished updating
while (numCompleted != numObjects)
{
for (int i = 0; i < numObjects; ++i)
{
auto objectRef = mgr.getObjectByIndex(i);
if (!objectRef->isLocked()) // if object is not locked, it is finished updating
{
std::cout << "Object " << objectRef->getId() << " completed. Value = " << objectRef->getValue() << std::endl;
mgr.removeObjectByIndex(i);
numCompleted++;
}
}
}
system("pause");
}
Looks like you've got a thread that is trying to join itself.
While I was trying to understand your solution I was simplifying it a lot. And I come to point that you use std::thread::join() method in a wrong way.
std::thread provide capabilities to wait for it completion (non-spin wait) -- In your example you wait for thread completion in infinite loop (snip wait) that will consume CPU time heavily.
You should call std::thread::join() from other thread to wait for thread completion. Mutex in Object in your example is not necessary. Moreover, you missed one mutex to synchronize access to std::cout, which is not thread-safe. I hope the example below will help.
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <thread>
#include <mutex>
#include <chrono>
#include <cassert>
// cout is not thread-safe
std::recursive_mutex cout_mutex;
class Object {
public:
Object(int sleepTime, unsigned int id)
: _sleepTime(sleepTime), _id(id), _value(0) {}
void runUpdate() {
if (!_thread.joinable())
_thread = std::thread(&Object::_update, this);
}
void waitForResult() {
_thread.join();
}
unsigned int getId() const { return _id; }
unsigned int getValue() const { return _value; }
private:
void _update() {
{
{
std::lock_guard<std::recursive_mutex> lock(cout_mutex);
std::cout << "thread " << _id << " sleeping for " << _sleepTime << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(_sleepTime));
_value = _id * 10;
}
std::lock_guard<std::recursive_mutex> lock(cout_mutex);
std::cout << "Object " << getId() << " completed. Value = " << getValue() << std::endl;
}
unsigned int _sleepTime;
unsigned int _id;
unsigned int _value;
std::thread _thread;
};
class ObjectManager : public std::vector<std::shared_ptr<Object>> {
public:
void runUpdate() {
for (auto it = this->begin(); it != this->end(); ++it)
(*it)->runUpdate();
}
void waitForAll() {
auto it = this->begin();
while (it != this->end()) {
(*it)->waitForResult();
it = this->erase(it);
}
}
};
int main(int argc, char* argv[]) {
enum {
TEST_OBJECTS_NUM = 2,
};
srand(static_cast<unsigned int>(time(nullptr)));
ObjectManager mgr;
// Generate sleep time for each object
std::vector<int> objectSleepTimes;
objectSleepTimes.reserve(TEST_OBJECTS_NUM);
for (int i = 0; i < TEST_OBJECTS_NUM; ++i)
objectSleepTimes.push_back(rand() * 9 / RAND_MAX + 1); // 1..10 seconds
// Create some objects
for (int i = 0; i < TEST_OBJECTS_NUM; ++i)
mgr.push_back(std::make_shared<Object>(objectSleepTimes[i], i));
assert(mgr.size() == TEST_OBJECTS_NUM);
// Print expected object completion order
// Sort from smallest to largest
std::sort(objectSleepTimes.begin(), objectSleepTimes.end());
for (size_t i = 0; i < mgr.size(); ++i)
std::cout << objectSleepTimes[i] << ", ";
std::cout << std::endl;
// Update objects
mgr.runUpdate();
mgr.waitForAll();
//system("pause"); // use Ctrl+F5 to run the app instead. That's more reliable in case of sudden app exit.
}
About is it a reasonable thing to do...
A better approach is to create an object update queue. Objects that need to be updated are added to this queue, which can be fulfilled by a group of threads instead of one thread per object.
The benefits are:
No 1-to-1 correspondence between thread and objects. Creating a thread is a heavy operation, probably more expensive than most update code for a single object.
Supports thousands of objects: with your solution you would need to create thousands of threads, which you will find exceeds your OS capacity.
Can support additional features like declaring dependencies between objects or updating a group of related objects as one operation.

Multi threaded program hangs on condition wait

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)

Producer and consumer functions for test thread-safe stack examples of C++ concurrency in action book

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.