In the example below, suppose I have a multithreaded queue i.e. supports multiple writes and reads from different threads (e.g. using mutex). We can see that 1. the pointer this as well as 2. the shared pointer m_mulque are passed to the readers and writers and my question is: Dereferencing the pointers this and m_mulque is thread safe or not? In other words is my following code thread safe or not meaning should I worry about any undefined behaviour if I run this please?
#include <mutex>
#include <queue>
struct multithreaded_queue
{
void push(const std::size_t i)
{
std::lock_guard lock(m_mutex);
m_queue.push(i);
};
void try_pop()
{
std::lock_guard lock(m_mutex);
m_queue.pop();
};
private:
std::queue<std::size_t> m_queue;
mutable std::mutex m_mutex;
};
class example
{
public:
example()
{
m_mulque = std::make_shared<multithreaded_queue>();
};
void run()
{
auto writer = [this]()
{
for (std::size_t i = 0; i < 1000; i++)
{
m_mulque->push(i);
};
};
auto reader = [this]()
{
for (std::size_t i = 0; i < 1000; i++)
{
m_mulque->try_pop();
};
};
std::thread writer1(writer);
std::thread reader1(reader);
std::thread reader2(reader);
writer1.join();
reader1.join();
reader2.join();
};
private:
std::shared_ptr<multithreaded_queue> m_mulque;
};
int main(int argc, char* argv[])
{
example ex;
ex.run(); //Is this thread safe to call?
};
Related
I am trying to write a thread safe datastore class.
This class object is shared with between many threads in Generator and Consumer, where the class members can be set or get.
By calling setDatastore() the object is set for usage at different threads.
Below is my code,
#ifndef IF_DATA_STORE_H
#define IF_DATA_STORE_H
#include <mutex>
#include <shared_mutex>
#include <memory>
class DataType1{public:int value;};
class DataType2{public:int value;};
class DataStore
{
public:
DataStore(): _member1(), _member2(){}
~DataStore(){}
// for member1
void setMember1(const DataType1& val)
{
std::unique_lock lock(_mtx1); // no one can read/write!
_member1 = val;
}
const DataType1& getMember1() const
{
std::shared_lock lock(_mtx1); // multiple threads can read!
return _member1;
}
// for member2
void setMember2(const DataType2& val)
{
std::unique_lock lock(_mtx2); // no one can read/write!
_member2 = val;
}
const DataType2& getMember2() const
{
std::shared_lock lock(_mtx2); // multiple threads can read!
return _member2;
}
private:
mutable std::shared_mutex _mtx1;
mutable std::shared_mutex _mtx2;
DataType1 _member1;
DataType2 _member2;
// different other member!
};
// now see where data is generated/consumed!
class Generator
{
public:
void start(){/* start thread!*/}
void setDataStore(std::shared_ptr<DataStore> store)
{
_store = store;
}
void threadRoutine() //this is called from different thread and updating values
{
// some code...
{
_data.value = 10; // keep a local updated copy of data!
_store->setMember1(_data);
}
}
private:
std::shared_ptr<DataStore> _store;
DataType1 _data;
};
class Consumer
{
public:
void start(){/* start thread!*/}
void setDataStore(std::shared_ptr<DataStore> store)
{
_store = store;
}
void threadRoutine() // running a check on datastore every 1sec
{
// some code...
auto val = _store->getMember1();
// do something..
}
private:
std::shared_ptr<DataStore> _store;
};
// fianlly start all!
int main()
{
// somewhere in main thread
std::shared_ptr<DataStore> store;
Consumer c; Generator g;
c.setDataStore(store); c.start();
g.setDataStore(store); g.start();
}
#endif
Questions:
Is there any other way than creating multiple shared mutex for each member?
In Generator.threadRoutine() if I keep a local copy of DataType1 does this cause high memory issues (I see high cpu and memory) when this block called frequently, don't if this is the root cause of it.
Any other better way suggested?
In some file called Tasks.h, I have the following function :-
void source_thread_func(BlockingQueue<Task> &bq, int num_ints)
{
std::cout<<"On source thread func"<<std::endl; // Debug
for (int i = 1; i <= num_ints; i++)
{
//Valgrind does not like this
std::unique_ptr<Task> task(new Task(i, i == num_ints));
std::cout<<"Pushing value = "<<i<<std::endl; // Debug
bq.push(task);
Task* tp = task.release();
assert (task.get() == nullptr);
delete tp;
}
}
and the relevant push function in the BlockingQueue is
void push(std::unique_ptr<T>& item)
{
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push(std::move(item));
mlock.unlock();
cond_.notify_one();
}
But, this still causes a leak when checking with Valgrind. Could you tell me where the leak is? I am attaching a screenshot of the valgrind result. How more can I delete this pointer?
Edit : Task doesn't contain a copy constructor (I've deleted it)
Further Edit : full example
//Tasks.h
namespace threadsx
{
class Task
{
public:
Task(int val, bool sentinel = false)
{
m_val = val;
Sent = sentinel;
}
int m_val;
int Sent;
//disable copying
Task (const Task&) = delete;
};
void source_thread_func(BlockingQueue<Task> &bq, int num_ints)
{
std::cout<<"On source thread func"<<std::endl; // Debug
for (int i = 1; i <= num_ints; i++)
{
std::unique_ptr<Task> task(new Task(i, i == num_ints));
std::cout<<"Pushing value = "<<i<<std::endl; // Debug
bq.push(task);
Task* tp = task.release();
assert (task.get() == nullptr);
delete tp;
}
}
}
+++++++++++++++++++++++++++++++
///BlockingQueue.h
namespace threadsx
{
// -- Custom Blocking Q
template <typename T>
class BlockingQueue
{
private:
std::queue<std::unique_ptr<T>> queue_;
std::mutex mutex_;
std::condition_variable cond_;
void push(std::unique_ptr<T>& item)
{
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push(std::move(item));
mlock.unlock();
cond_.notify_one();
}
BlockingQueue()=default;
BlockingQueue(const BlockingQueue&) = delete; // disable copying
BlockingQueue& operator=(const BlockingQueue&) = delete; // disable assignment
};
}
+++++++++++++++++++++++++++++++
//main.cpp
int main(int argc, char **argv)
{
int num_ints = 30;
int threshold = 5;
threadsx::BlockingQueue<threadsx::Task> q;
std::vector<int> t;
std::thread source_thread(threadsx::source_thread_func, std::ref(q), num_ints);
if(source_thread.joinable())
source_thread.join();
return 0;
}
The program that you show does not delete the Task that was allocated. push moves the ownership away from task, so tp is always null.
The ownership of the resource is transferred into queue_, and how that pointer is leaked (assuming valgrind is correct) is not shown in the example program.
Few quality issues:
As pointed out in the comments, it is usually a bad design to pass unique pointers by non-const reference. Pass by value when you intend to transfer ownership.
I've deleted the copy constructor on Task. Would passing by value still work?
Whether Task is copyable is irrelevant to whether a unique pointer can be passed by value. Unique pointer is movable regardless of the type of the pointed object, and therefore can be passed by value.
Don't release from a unique pointer just in order to delete the memory. Simply let the unique pointer go out of scope - its destructor takes care of deletion.
You are not allowed to delete the raw task, since the ownership is no longer yours.
void source_thread_func(BlockingQueue<Task>& bq, int num_ints)
{
std::cout<<"On source thread func"<<std::endl; // Debug
for (int i = 1; i <= num_ints; i++)
{
std::unique_ptr<Task> task = std::make_unique<Task>(i, i == num_ints);
bq.push(std::move(task));
}
}
Blocking Queue:
#include <memory>
#include <mutex>
#include <condition_variable>
#include <deque>
template <typename T>
class BlockingQueue {
public:
void push(std::unique_ptr<T>&& item)
{
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push_back(std::move(item));
cond_.notify_one();
}
std::unique_ptr<T> pop()
{
std::unique_lock<std::mutex> mlock(mutex_);
if (queue_.empty()) {
cond_.wait(mlock, [this] { return !queue_.empty(); });
}
std::unique_ptr<T> ret = std::unique_ptr<T>(queue_.front().release());
queue_.pop_front();
return ret;
}
private:
std::deque<std::unique_ptr<T>> queue_;
std::mutex mutex_;
std::condition_variable cond_;
};
If you want to spare yourself the headache of std::move, use shared_ptr instead
struct Info {
int a;
int b;
};
class A {
public:
A() {
_info = new Info;
_info->a = 1;
_info->b = 1;
}
void update()
{
std::lock_guard<std::mutex> l(_m);
_info->a = 2;
_info->b = 3;
}
Info* get_info() {
std::lock_guard<std::mutex> l(_m);
return _info;
}
private:
mutable std::mutex _m;
Info* _info;
};
A a;
// in thread 1
a.update();
// in thread 2
Info* i = a.get_info();
std::cout<<i->a<<" "<<i->b<<std::endl;
Does it possible to print 1 3 or 2 1 ?
This is unspecified/undefined behavior
The objects being accessed are not protected by a mutex. The mutex is being held only long enough to read the pointer. The mutex is not held while accessing the contents of the pointer, as such the mutex offers no synchronization with update().
I am attempting to pass data between two sub classes of my main threading class using boost::thread. I am very new to multithreading and in a bit over my head. Right now the code produces the following repeatedly.
Reader Api: 0
Below is my class andd subclass definitions. The threading class is a singleton.
class threading
{
public:
static threading *Instance();
void workerFunc();
void workerFunc2();
void inputWorkerFunc(); // handles input processing
void producer();
void consumer();
int getGlobalVariable() // retrieves the value of globalVariable
{
return (globalVariable);
}
void setGlobalVariable(int set) // sets the value of globalVariable
{
globalVariable = set;
}
class Reader
{
public:
Reader(int waitTime) { _waitTime = waitTime;}
void operator() ()
{
threading *thread = threading::Instance();
int globalVariable = thread->getGlobalVariable();
for (int i=0; i < 10; i++)
{
logMsg("Reader Api: " +Ogre::StringConverter::toString(globalVariable));
// usleep(_waitTime);
boost::this_thread::sleep(boost::posix_time::microseconds(_waitTime));
}
return;
}
private:
int _waitTime;
};
class Writer
{
public:
Writer(int variable, int waitTime)
{
_writerVariable = variable;
logMsg("Writer Variable: " +Ogre::StringConverter::toString(_writerVariable));
_waitTime = waitTime;
}
void operator () ()
{
logMsg("Writer Variable: " +Ogre::StringConverter::toString(_writerVariable));
threading *thread = threading::Instance();
int globalVariable = thread->getGlobalVariable();
for (int i=0; i < 10; i++)
{
// usleep(_waitTime);
boost::this_thread::sleep(boost::posix_time::microseconds(_waitTime));
logMsg("Waittime Variable: " +Ogre::StringConverter::toString(_waitTime));
// Take lock and modify the global variable
boost::mutex::scoped_lock lock(_writerMutex);
globalVariable = _writerVariable;
thread->setGlobalVariable(globalVariable);
_writerVariable++;
// since we have used scoped lock,
// it automatically unlocks on going out of scope
}
logMsg("Writer Variable: " +Ogre::StringConverter::toString(_writerVariable));
// thread->setGlobalVariable(globalVariable);
}
private:
int _writerVariable;
int _waitTime;
static boost::mutex _writerMutex;
};
protected:
threading();
threading(const threading&);
threading& operator= (const threading&);
private:
static threading *pInstance;
int globalVariable;
boost::mutex mutex;
boost::condition_variable condvar;
typedef boost::unique_lock<boost::mutex> lockType;
double value;
int count;
};
Here is how I am calling the classes in my main code:
threading *thread = threading::Instance();
threading::Reader reads(100);
threading::Writer writes1(100, 200);
threading::Writer writes2(200, 200);
boost::thread readerThread(reads);
boost::thread writerThread1(writes1);
boost::this_thread::sleep(boost::posix_time::microseconds(100));
boost::thread writerThread2(writes2);
readerThread.join();
writerThread1.join();
writerThread2.join();
You're obviously missing a decent synchronization mechanism like a (global) mutex, to prevent race conditions with this code:
static std::mutex globalVariableProtector;
int getGlobalVariable() // retrieves the value of globalVariable
{
std::lock_guard<std::mutex> lock(globalVariableProtector);
return (globalVariable);
}
void setGlobalVariable(int set) // sets the value of globalVariable
{
std::lock_guard<std::mutex> lock(globalVariableProtector);
globalVariable = set;
}
Moreover, you actually should consider to place that variable into the producer thread's class, and provide getter/setter functions for it, instead of using a global variable.
I've been sharing the current standard references, rather boost. Boost may have their own mechanisms of lock_guards and Synchronized Data Structures, if you for reason can't use the current c++ standard.
With multiple threads (std::async) sharing an instance of the following class through a shared_ptr, is it possible to get a segmentation fault in this part of the code? If my understanding of std::mutex is correct, mutex.lock() causes all other threads trying to call mutex.lock() to block until mutex.unlock() is called, thus access to the vector should happen purely sequentially. Am I missing something here? If not, is there a better way of designing such a class (maybe with a std::atomic_flag)?
#include <mutex>
#include <vector>
class Foo
{
private:
std::mutex mutex;
std::vector<int> values;
public:
Foo();
void add(const int);
int get();
};
Foo::Foo() : mutex(), values() {}
void Foo::add(const int value)
{
mutex.lock();
values.push_back(value);
mutex.unlock();
}
int Foo::get()
{
mutex.lock();
int value;
if ( values.size() > 0 )
{
value = values.back();
values.pop_back();
}
else
{
value = 0;
}
mutex.unlock();
return value;
}
Disclaimer: The default value of 0 in get() is intended as it has a special meaning in the rest of the code.
Update: The above code is exactly as I use it, except for the typo push_Back of course.
Other than not using RAII to acquire the lock and using size() > 0 instead of !empty(), the code looks fine. This is exactly how a mutex is meant to be used and this is the quintessential example of how and where you need a mutex.
As Andy Prowl pointed out, instances can't be copy constructed or copy assigned.
Here is the "improved" version:
#include <mutex>
#include <vector>
class Foo {
private:
std::mutex mutex;
typedef std::lock_guard<std::mutex> lock;
std::vector<int> values;
public:
Foo();
void add(int);
int get();
};
Foo::Foo() : mutex(), values() {}
void Foo::add(int value) {
lock _(mutex);
values.push_back(value);
}
int Foo::get() {
lock _(mutex);
int value = 0;
if ( !values.empty() )
{
value = values.back();
values.pop_back();
}
return value;
}
with RAII for acquiring the mutex etc.