I am changing a single thread program into multi thread using boost:thread library. The program uses unordered_map as a hasp_map for lookups. My question is..
At one time many threads will be writing, and at another many will be reading but not both reading and writing at the same time i.e. either all the threads will be reading or all will be writing. Will that be thread safe and the container designed for this? And if it will be, will it really be concurrent and improve performance? Do I need to use some locking mechanism?
I read somewhere that the C++ Standard says the behavior will be undefined, but is that all?
UPDATE: I was also thinking about Intel concurrent_hash_map. Will that be a good option?
STL containers are designed so that you are guaranteed to be able to have:
A. Multiple threads reading at the same time
or
B. One thread writing at the same time
Having multiple threads writing is not one of the above conditions and is not allowed. Multiple threads writing will thus create a data race, which is undefined behavior.
You could use a mutex to fix this. A shared_mutex (combined with shared_locks) would be especially useful as that type of mutex allows multiple concurrent readers.
http://eel.is/c++draft/res.on.data.races#3 is the part of the standard which guarantees the ability to concurrently use const functions on different threads. http://eel.is/c++draft/container.requirements.dataraces specifies some additional non-const operations which are safe on different threads.
std::unordered_map meets the requirements of Container (ref http://en.cppreference.com/w/cpp/container/unordered_map). For container thread safety see: http://en.cppreference.com/w/cpp/container#Thread_safety.
Important points:
"Different elements in the same container can be modified concurrently by different threads"
"All const member functions can be called concurrently by different threads on the same container. In addition, the member functions begin(), end(), rbegin(), rend(), front(), back(), data(), find(), lower_bound(), upper_bound(), equal_range(), at(), and, except in associative containers, operator[], behave as const for the purposes of thread safety (that is, they can also be called concurrently by different threads on the same container)."
Will that be thread safe and the container designed for this?
No, the standard containers are not thread safe.
Do I need to use some locking mechanism?
Yes, you do. Since you're using boost, boost::mutex would be a good idea; in C++11, there's std::mutex.
I read somewhere that the C++ Standard says the behavior will be undefined, but is that all?
Indeed, the behaviour is undefined. I'm not sure what you mean by "is that all?", since undefined behaviour is the worst possible kind of behaviour, and a program that exhibits it is by definition incorrect. In particular, incorrect thread synchronisation is likely to lead to random crashes and data corruption, often in ways that are very difficult to diagnose, so you would be wise to avoid it at all costs.
UPDATE: I was also thinking about Intel concurrent_hash_map. Will that be a good option?
It sounds good, but I've never used it myself so I can't offer an opinion.
The existing answers cover the main points:
you must have a lock to read or write to the map
you could use a multiple-reader / single-writer lock to improve concurrency
Also, you should be aware that:
using an earlier-retrieved iterator, or a reference or pointer to an item in the map, counts as a read or write operation
write operations performed in other threads may invalidate pointers/references/iterators into the map, much as they would if they were done in the same thread, even if a lock is again acquired before an attempt is made to continue using them...
You can use concurrent_hash_map or employ an mutex when you access unordered_map. one of issue on using intel concurrent_hash_map is you have to include TBB, but you already use boost.thread. These two components have overlapped functionality, and hence complicate your code base.
std::unordered_map is a good fit for some multi-threaded situations.
There are also other concurrent maps from Intel TBB:
tbb:concurrent_hash_map. It supports fine-grained, per-key locking for insert/update, which is something that few other hashmaps can offer. However, the syntax is slightly more wordy. See full sample code. Recommended.
tbb:concurrent_unordered_map. It is essentially the same thing, a key/value map. However, it is much lower level, and more difficult to use. One has to supply a hasher, a equality operator, and an allocator. There is no sample code anywhere, even in the official Intel docs. Not recommended.
If you don't need all of the functionality of unordered_map, then this solution should work for you. It uses mutex to control access to the internal unordered_map. The solution supports the following methods, and adding more should be fairly easy:
getOrDefault(key, defaultValue) - returns the value associated with key, or defaultValue if no association exists. A map entry is not created when no association exists.
contains(key) - returns a boolean; true if the association exists.
put(key, value) - create or replace association.
remove(key) - remove association for key
associations() - returns a vector containing all of the currently known associations.
Sample usage:
/* SynchronizedMap
** Functional Test
** g++ -O2 -Wall -std=c++11 test.cpp -o test
*/
#include <iostream>
#include "SynchronizedMap.h"
using namespace std;
using namespace Synchronized;
int main(int argc, char **argv) {
SynchronizedMap<int, string> activeAssociations;
activeAssociations.put({101, "red"});
activeAssociations.put({102, "blue"});
activeAssociations.put({102, "green"});
activeAssociations.put({104, "purple"});
activeAssociations.put({105, "yellow"});
activeAssociations.remove(104);
cout << ".getOrDefault(102)=" << activeAssociations.getOrDefault(102, "unknown") << "\n";
cout << ".getOrDefault(112)=" << activeAssociations.getOrDefault(112, "unknown") << "\n";
if (!activeAssociations.contains(104)) {
cout << 123 << " does not exist\n";
}
if (activeAssociations.contains(101)) {
cout << 101 << " exists\n";
}
cout << "--- associations: --\n";
for (auto e: activeAssociations.associations()) {
cout << e.first << "=" << e.second << "\n";
}
}
Sample output:
.getOrDefault(102)=green
.getOrDefault(112)=unknown
123 does not exist
101 exists
--- associations: --
105=yellow
102=green
101=red
Note1: The associations() method is not intended for very large datasets as it will lock the table during the creation of the vector list.
Note2: I've purposefully not extended unordered_map in order to prevent my self from accidentally using a method from unordered_map that has not been extended and therefore might not be thread safe.
#pragma once
/*
* SynchronizedMap.h
* Wade Ryan 20200926
* c++11
*/
#include <unordered_map>
#include <mutex>
#include <vector>
#ifndef __SynchronizedMap__
#define __SynchronizedMap__
using namespace std;
namespace Synchronized {
template <typename KeyType, typename ValueType>
class SynchronizedMap {
private:
mutex sync;
unordered_map<KeyType, ValueType> usermap;
public:
ValueType getOrDefault(KeyType key, ValueType defaultValue) {
sync.lock();
ValueType rs;
auto value=usermap.find(key);
if (value == usermap.end()) {
rs = defaultValue;
} else {
rs = value->second;
}
sync.unlock();
return rs;
}
bool contains(KeyType key) {
sync.lock();
bool exists = (usermap.find(key) != usermap.end());
sync.unlock();
return exists;
}
void put(pair<KeyType, ValueType> nodePair) {
sync.lock();
if (usermap.find(nodePair.first) != usermap.end()) {
usermap.erase(nodePair.first);
}
usermap.insert(nodePair);
sync.unlock();
}
void remove(KeyType key) {
sync.lock();
if (usermap.find(key) != usermap.end()) {
usermap.erase(key);
}
sync.unlock();
}
vector<pair<KeyType, ValueType>> associations() {
sync.lock();
vector<pair<KeyType, ValueType>> elements;
for (auto it=usermap.begin(); it != usermap.end(); ++it) {
pair<KeyType, ValueType> element (it->first, it->second);
elements.push_back( element );
}
sync.unlock();
return elements;
}
};
}
#endif
Related
Is such a piece of code safe?
vector<int> v;
void thread_1()
{
v.push_back(100);
}
void thread_2()
{
for (int i : v)
{
// some actions
}
}
Does for (int i : v) compile into something like:
for ( ; __begin != __end; ++__begin)
{
int i = *__begin;
}
Is it possible that push_back in first thread will make data reallocation (when size == capacity), remove old data and *__begin in another thread will dereference freed iterator? And a runtime crash will occur
If so, how should I synchronize the threads? Something like:
void thread_1()
{
mtx.lock();
v.push_back(100);
mtx.unlock();
}
void thread_2()
{
mtx.lock();
for (int i : v)
{
// some actions
}
mtx.unlock();
}
?
Simply put, On some architectures, Fundamental types are inherently atomic, while on others they are not.
On those architectures, writing and reading from and to vector<int> v is thread safe as long as no reallocation occurs and ints are properly aligned; but it depends on various factors.
BUT:
you may want to avoid writing architecture-specific code (unless you want your code to basically run only on your own computer)
You have no mechanism in your code to prevent reallocation (which may invalidate iterators held by other threads), and since you also have no mechanism to synchronize the threads in your code, such reallocations can easily occur.
Considering your design, if you have a std::vector of classes/structs instead of Fundamental Types, you will also risk race conditions and/or UB even in simple concurrent read/writes since one thread can see the vector's element in a broken state (i.e. thread2 can see element#x in vector while it is being changed{push_back'd} by thread1)
in order to ensure thread safety, you have many options:
Prevent modifications to the queue entirely while its being read/written to - basically what you are doing in you own solution- using a global mutual exclusion mechanism
Prevent modifications by other threads for the element currently being manipulated using fine-grained mutual exclusion (could get tricky for linked data structures)
Use thread-safe data structure which have built-in mechanisms to ensure that a single element cannot be accessed by multiple threads
...
Example code.
class Obj
{
public:
void doSome(void)
{
std::cout << "Hello World!" << std::endl;
}
};
std::unordered_map<int, std::unique_ptr<Obj>> map;
// insert -- done with single thread and before find()
map[123] = std::move( std::unique_ptr<Obj>(new Obj) );
// find -- run from multiple threads
auto search = map.find(123); // <=== (Q)
if (search != map.end())
{
search->second->doSome();
}
(Q)
How about the thread safty if there are multiple threads running //find section with map.find(123)?
will map.find(123) always find the obj in every thread? as long as the search->second not assigned to someone else?
When more than one thread accesses the same variable and at least one of them writes to it you have a data race. That's not the case here, where everyone is reading the same data. That's okay. There's another issue, though, which isn't addressed in this code: depending on when the data is stored into the map object, some threads might not see the updated version of the map object. The simplest way to deal with this synchronization problem is to set up the map object before creating any of the reader threads.
Neither find(), nor any other method in the unordered map is thread safe. If it's possible for one execution thread to call find() while any other thread calls any unordered map method that modifies it, this results in undefined behavior.
If multiple execution threads are calling find() with the same key, provided that there is no undefined behavior all execution threads will get the same value for that key.
The following is a snippet of a larger program and is done using Pthreads.
The UpdateFunction reads from a text file. The FunctionMap is just used to output (key,1). Here essentially UpdateFunction and FunctionMap run on different threads.
queue <list<string>::iterator> mapperpool;
void *UpdaterFunction(void* fn) {
std::string *x = static_cast<std::string*>(fn);
string filename = *x;
ifstream file (filename.c_str());
string word;
list <string> letterwords[50];
char alphabet = '0';
bool times = true;
int charno=0;
while(file >> word) {
if(times) {
alphabet = *(word.begin());
times = false;
}
if (alphabet != *(word.begin())) {
alphabet = *(word.begin());
mapperpool.push(letterwords[charno].begin());
letterwords[charno].push_back("xyzzyspoon");
charno++;
}
letterwords[charno].push_back(word);
}
file.close();
cout << "UPDATER DONE!!" << endl;
pthread_exit(NULL);
}
void *FunctionMap(void *i) {
long num = (long)i;
stringstream updaterword;
string toQ;
int charno = 0;
fprintf(stderr, "Print me %ld\n", num);
sleep(1);
while (!mapperpool.empty()) {
list<string>::iterator it = mapperpool.front();
while(*it != "xyzzyspoon") {
cout << "(" << *it << ",1)" << "\n";
cout << *it << "\n";
it++;
}
mapperpool.pop();
}
pthread_exit(NULL);
}
If I add the while(!mapperpool.empty()) in the UpdateFunction then it gives me the perfect output. But when I move it back to the FunctionMap then it gives me a weird out and Segfaults later.
Output when used in UpdateFunction:
Print me 0
course
cap
class
culture
class
cap
course
course
cap
culture
concurrency
.....
[Each word in separate line]
Output when used in FunctionMap (snippet shown above):
Print me 0
UPDATER DONE!!
(course%0+�0#+�0�+�05P+�0����cap%�+�0�+�0,�05�+�0����class5P?�0
����xyzzyspoon%�+�0�+�0(+�0%P,�0,�0�,�05+�0����class%p,�0�,�0-�05�,�0����cap%�,�0�,�0X-�05�,�0����course%-�0 -�0�-�050-�0����course%-�0p-�0�-�05�-�0����cap%�-�0�-�0H.�05�-�0����culture%.�0.�0�.�05 .�0
����concurrency%P.�0`.�0�.�05p.�0����course%�.�0�.�08/�05�.�0����cap%�.�0/�0�/�05/�0Segmentation fault (core dumped)
How do I fix this issue?
list <string> letterwords[50] is local to UpdaterFunction. When UpdaterFunction finishes, all its local variables got destroyed. When FunctionMap inspects iterator, that iterator already points to deleted memory.
When you insert while(!mapperpool.empty()) UpdaterFunction waits for FunctionMap completion and letterwords stays 'alive'.
Here essentially UpdateFunction and FunctionMap run on different threads.
And since they both manipulate the same object (mapperpool) and neither of them uses either pthread_mutex nor std::mutex (C++11), you have a data race. If you have a data race, you have Undefined Behaviour and the program might do whatever it wants. Most likely it will write garbage all over memory until eventually crashing, exactly as you see.
How do I fix this issue?
By locking the mapperpool object.
Why is list not thread-safe?
Well, in vast majority of use-cases, a single list (or any other collection) won't be used by more than one thread. In significant part of the rest the lock will have to extend over more than one operation on the collection, so the client will have to do its own locking anyway. The remaining tiny percentage of cases where locking in the operations themselves would help is not worth adding the overhead for everyone; C++ key design principle is that you only pay for what you use.
The collections are only reentrant, meaning that using different instances in parallel is safe.
Note on pthreads
C++11 introduced threading library that integrates well with the language. Most notably, it uses RAII for locking of std::mutex via std::lock_guard, std::unique_lock and std::shared_lock (for reader-writer locking). Consistently using these can eliminate large class of locking bugs that otherwise take considerable time to debug.
If you can't use C++11 yet (on desktop you can, but some embedded platforms did not get a compiler update yet), you should first consider Boost.Thread as it provides the same benefits.
If you can't use even then, still try to find, or write, a simple RAII wrapper for locking like the C++11/Boost do. The basic wrapper is just a couple of lines, but it will save you a lot of debugging.
Note that C++11 and Boost also have atomic operations library that pthreads sorely miss.
I'm trying to write a map that is thread-safe, but never locks or blocks on read. My attempt is to use a read-only map that gets copied out on write. The idea is get() is lock-free, and put() copies the current read-only underlying map to a new one, does the put, and swaps out the current underlying map for a new one. (yes, put() is inefficient since it copies the entire map, but I do not care for my use case)
My first stab at this used std::atomic<*StringMap> for the read-only map BUT there is a huge bug with this, probably due to my java background. get() atomically gets a pointer to the underlying map, which may or may not be the current one when it loads it (which is ok). But put() deletes the underlying map after it swaps it out for the new one. If get() is calling the old map just as it gets deleted, this will obviously crash.
A friend of mine suggested shared_ptr, but he's not sure if the shared_ptr operations do any locking under the covers. The docs say it is thread-safe, though. Edit: As nosid points out, it is not thread safe and I need the special atomic operation from std::atomic.
So my questions are: 1. Is this algorithm viable? 2. Do shared_ptr operations do any locking, especially on access?
#include <unordered_map>
#include <atomic>
#include <pthread.h>
#include <memory>
typedef std::unordered_map<std::string, std::string> StringMap;
class NonBlockingReadMap {
private:
pthread_mutex_t fMutex;
std::shared_ptr<StringMap> fspReadMapReference;
public:
NonBlockingReadMap() {
fspReadMapReference = std::make_shared<StringMap>();
}
~NonBlockingReadMap() {
//so, nothing here?
}
std::string get(std::string &key) {
//does this access trigger any locking?
return fspReadMapReference->at(key);
}
void put(std::string &key, std::string &value) {
pthread_mutex_lock(&fMutex);
std::shared_ptr<StringMap> spMapCopy = std::make_shared<StringMap>(*fspReadMapReference);
std::pair<std::string, std::string> kvPair(key, value);
spMapCopy->insert(kvPair);
fspReadMapReference.swap(spMapCopy);
pthread_mutex_unlock(&fMutex);
}
void clear() {
pthread_mutex_lock(&fMutex);
std::shared_ptr<StringMap> spMapCopy = std::make_shared<StringMap>(*fspReadMapReference);
fspReadMapReference.swap(spMapCopy);
spMapCopy->clear();
pthread_mutex_unlock(&fMutex);
}
};
Your code contains a data race on the std::shared_ptr, and the behaviour of programs with data races is undefined in C++.
The problem is: The class std::shared_ptr is not thread-safe. However, there are special atomic operations for std::shared_ptr, which can be used to solve the problem.
You can find more information about these atomic operations on the following webpage:
http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
Reader should do two operations: get and put. get always retrieves the new pointer and increments an atomic count. Put releases the pointer and decrements. Delete the map when the count goes to zero.
The writer creates a new map and does a get on it. It then does a put on the old map to mark it for delete.
I'm trying to implement what I think is a fairly simple design. I have a bunch of objects, each containing a std::map and there will be multiple processes accessing them. I want to make sure that there is only one insert/erase to each of these maps at a time.
So I've been reading about boost::thread and class member mutexes and using bind to pass to class member which are all new things to me. I started with a simple example from a Dr. Dobbs article and tried modifying that. I was getting all kinds of compiler errors due to my Threaded object having to be noncopyable. After reading up on that, I decided I can avoid the hassle by keeping a pointer to a mutex instead. So now I have code that compiles but results in the following error:
/usr/include/boost/shared_ptr.hpp:419:
T* boost::shared_ptr< <template-parameter-1-1> >::operator->() const
[with T = boost::mutex]: Assertion `px != 0' failed. Abort
Now I'm really stuck and would really appreciate help with the code as well as comments on where I'm going wrong conceptually. I realize there are some answered questions around these issues here already but I guess I'm still missing something.
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <map>
using namespace std;
class Threaded {
public:
std::map<int,int> _tsMap;
void count(int id) {
for (int i = 0; i < 100; ++i) {
_mx->lock();
//std::cout << id << ": " << i << std::endl;
_tsMap[i] ++;
_mx->unlock();
}
}
private:
boost::shared_ptr<boost::mutex> _mx;
};
int main(int argc, char* argv[]) {
Threaded th;
int i = 1;
boost::thread thrd1(boost::bind(&Threaded::count, &th, 1));
//boost::thread thrd2(boost::bind(&th.count, 2));
thrd1.join();
//thrd2.join();
return 0;
}
It looks like you're missing a constructor in your Threaded class that creates the mutex that _mx is intended to point at. In its current state (assuming you ran this code just as it is), the default constructor for Threaded calls the default constructor for shared_ptr, resulting in a null pointer (which is then dereferenced in your count() function.
You should add a constructor along the following lines:
Threaded::Threaded(int id)
: _mx(new boost::mutex())
, _mID(id)
{
}
Then you could remove the argument from your count function as well.
A mutex is non-copyable for good reasons. Trying to outsmart the compiler by using a pointer to a mutex is a really bad idea. If you succeed, the compiler will fail to notice the problems, but they will still be there and will turn round and bite you at runtime.
There are two solutions
store the mutex in your class as a static
store the mutex outside your class.
There are advantages for both - I prefer the second.
For some more discussion of this, see my answer here mutexes with objects
Conceptually, I think you do have a problem. Copying a std::shared_ptr will just increase its reference count, and the different objects will all use the same underlying mutex - meaning that whenever one of your objects is used, none of the rest of them can be used.
You, on the other hand, need each object to get its own mutex guard which is unrelated to other objects mutex guards.
What you need is to keep the mutex defined in the class private section as it is - but ensure that your copy constructor and copy assignment operator are overloaded to create a new one from scratch - one bearing no relation to the mutex in the object being copied/assigned from.