Я хочу добиться блокировки ar / w на std :: map, которая используется несколькими потоками.Сначала я обернул std :: map и взял boost :: shared_mutex в качестве члена класса и обернул операции вставки / стирания / [] для обеспечения безопасности потока.
Я использовал boost :: shared_lock и boost :: unique_lock для r/ w синхронизация.
Я могу вспомнить другой дизайн, где может быть создан поток чтения / записи, который будет работать исключительно на std :: map, но я подумал о способе, которым я блокирую / разблокирую чтение или записьфункции std :: map.дело в том, что я не достигаю синхронизации.
что мне здесь не хватает?я должен изменить дизайн?если да ... можете ли вы объяснить, почему этот дизайн не будет работать?
здесь приведена реализация блокировки ar / w https://coliru.stacked -crooked.com / a / 8a96d27cb0a3e60c
еще одно ускорение :: реализация мьютекса https://coliru.stacked -crooked.com / a / 717c26ee1917760a
для справки:
#include <unistd.h>
#include <map>
#include <vector>
#include <iostream>
#include <pthread.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
std::string gen_random(const int len) {
char gen_s[100];
static const char charset[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < len; ++i) {
gen_s[i] = charset[rand() % (sizeof(charset) - 1)];
}
gen_s[len] = 0;
std::string s = gen_s;
return s;
}
const int ITERS = 100;
template <typename T1, typename T2>
class ThreadSafeMap
{
private:
std::map<T1, T2> _map;
boost::shared_mutex _lockShared;
//boost::mutex _lock;
public:
ThreadSafeMap();
bool insert(std::pair<T1, T2> kv);
typename std::map<T1, T2>::const_iterator find(T1 k);
bool erase(T1 k);
T2 at(int index);
typename std::map<T1, T2>::iterator end();
typename std::map<T1, T2>::iterator begin();
T2& operator[](const T1& k);
//void operator=(T2 v);
};
template <typename T1, typename T2>
ThreadSafeMap<T1, T2>::ThreadSafeMap()
{
}
template <typename T1, typename T2>
bool ThreadSafeMap<T1, T2>::insert(std::pair<T1, T2> kv)
{
boost::unique_lock<boost::shared_mutex> _wrLock(this->_lockShared);
_map.insert(kv);
return true;
}
template <typename T1, typename T2>
typename std::map<T1, T2>::const_iterator ThreadSafeMap<T1, T2>::find(T1 k)
{
boost::shared_lock<boost::shared_mutex> _rdLock(this->_lockShared);
typename std::map<T1, T2>::const_iterator toReturn = _map.find(k);
return toReturn;
}
template <typename T1, typename T2>
bool ThreadSafeMap<T1, T2>::erase(T1 data)
{
boost::unique_lock<boost::shared_mutex> _wrLock(this->_lockShared);
_map.erase(data);
return true;
}
template <typename T1, typename T2>
T2 ThreadSafeMap<T1, T2>::at(int index)
{
boost::shared_lock<boost::shared_mutex> _rdLock(this->_lockShared);
T2 toReturn = _map.at(index);
return toReturn;
}
template <typename T1, typename T2>
typename std::map<T1, T2>::iterator ThreadSafeMap<T1, T2>::end()
{
boost::shared_lock<boost::shared_mutex> _rdLock(this->_lockShared);
typename std::map<T1, T2>::iterator toReturn = _map.end();
return toReturn;
}
template <typename T1, typename T2>
typename std::map<T1, T2>::iterator ThreadSafeMap<T1, T2>::begin()
{
boost::shared_lock<boost::shared_mutex> _rdLock(this->_lockShared);
typename std::map<T1, T2>::iterator toReturn = _map.begin();
return toReturn;
}
template <typename T1, typename T2>
T2& ThreadSafeMap<T1, T2>::operator[](const T1& k)
{
boost::unique_lock<boost::shared_mutex> _wrLock(this->_lockShared);
T2& toReturn = _map[k];
return toReturn;
}
ThreadSafeMap<std::string,std::string> mapp;
void writer(int threadNo)
{
while(true)
{
std::string key = gen_random(10);
std::string value = gen_random(10);
std::cout << threadNo << " putting " << key << ":" << value << std::endl;
mapp[key] = value;
usleep(100);
}
}
void reader(int threadNo)
{
while(true)
{
//std::map<std::string,std::string>::iterator it = mapp.begin();
std::string str;
if(mapp.begin() != mapp.end())
{
str = mapp.begin()->first;
std::cout << threadNo << " retrieved " << mapp.begin()->first << ":" << mapp.begin()->second << std::endl;
mapp.erase(str);
std::cout << threadNo << " erased " << str << std::endl;
}
//std::string str1 = it->first;
//mapp.erase(it->first);
//std::cout << threadNo << " erased " << it->first << std::endl;
usleep(100);
}
}
int main(int argc, char* argv[])
{
int n = 10;
std::vector<boost::thread> allThreadVector;
while(n--)
{
boost::thread t1 = boost::thread(&reader, 10-n);
boost::thread t2 = boost::thread(&writer,10-n);
allThreadVector.push_back(std::move(t1));
allThreadVector.push_back(std::move(t2));
}
for (boost::thread &th : allThreadVector)
{
// If thread Object is Joinable then Join that thread.
if (th.joinable())
{
std::cout << "joined : " << th.get_id();
th.join();
}
}
return 0;
}
оба эти способа генерируют данныегонки.
это один из результатов гонки данных:
1 putting nWlrBbmQBh:CDarzOwKkY
1 retrieved nWlrBbmQBh:CDarzOwKkY1 putting HIDdqSCDXr:JmOWFrxsjy
2 retrieved nWlrBbmQBh:CDarzOwKkY
2 erased nWlrBbmQBh
21 retrieved putting BldbEFSArC:BynEcdyGgx
==================
WARNING: ThreadSanitizer: data race (pid=23638)
Read of size 8 at 0x7b1800000050 by thread T1:
#0 fwrite ../../.././libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1048 (libtsan.so.0+0x31a18)
#1 std::basic_streambuf<char, std::char_traits<char> >::sputn(char const*, long) /root/orig/gcc-8.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/streambuf:458 (libstdc++.so.6+0x10ccc3)
#2 void std::__ostream_write<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) /root/orig/gcc-8.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream_insert.h:50 (libstdc++.so.6+0x10ccc3)
#3 std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) /root/orig/gcc-8.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream_insert.h:101 (libstdc++.so.6+0x10ccc3)
#4 void boost::_bi::list1<boost::_bi::value<int> >::operator()<void (*)(int), boost::_bi::list0>(boost::_bi::type<void>, void (*&)(int), boost::_bi::list0&, int) /usr/local/include/boost/bind/bind.hpp:259 (a+0x413a2b)
#5 boost::_bi::bind_t<void, void (*)(int), boost::_bi::list1<boost::_bi::value<int> > >::operator()() /usr/local/include/boost/bind/bind.hpp:1294 (a+0x4136ab)
#6 boost::detail::thread_data<boost::_bi::bind_t<void, void (*)(int), boost::_bi::list1<boost::_bi::value<int> > > >::run() /usr/local/include/boost/thread/detail/thread.hpp:116 (a+0x4132e2)
#7 thread_proxy <null> (libboost_thread.so.1.66.0+0x124fc)
Previous write of size 8 at 0x7b1800000050 by thread T2:
#0 memcpy ../../.././libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:737 (libtsan.so.0+0x308f5)
#1 std::char_traits<char>::copy(char*, char const*, unsigned long) /root/orig/gcc-8.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:350 (libstdc++.so.6+0x11ced1)
#2 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_S_copy(char*, char const*, unsigned long) /root/orig/gcc-8.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:340 (libstdc++.so.6+0x11ced1)
#3 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /root/orig/gcc-8.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.tcc:272 (libstdc++.so.6+0x11ced1)
#4 void boost::_bi::list1<boost::_bi::value<int> >::operator()<void (*)(int), boost::_bi::list0>(boost::_bi::type<void>, void (*&)(int), boost::_bi::list0&, int) /usr/local/include/boost/bind/bind.hpp:259 (a+0x413a2b)
#5 boost::_bi::bind_t<void, void (*)(int), boost::_bi::list1<boost::_bi::value<int> > >::operator()() /usr/local/include/boost/bind/bind.hpp:1294 (a+0x4136ab)
#6 boost::detail::thread_data<boost::_bi::bind_t<void, void (*)(int), boost::_bi::list1<boost::_bi::value<int> > > >::run() /usr/local/include/boost/thread/detail/thread.hpp:116 (a+0x4132e2)
#7 thread_proxy <null> (libboost_thread.so.1.66.0+0x124fc)
Thread T1 (tid=23640, running) created by main thread at:
#0 pthread_create ../../.././libsanitizer/tsan/tsan_interceptors.cc:915 (libtsan.so.0+0x2af6b)
#1 boost::thread::start_thread_noexcept() <null> (libboost_thread.so.1.66.0+0x11879)
#2 boost::thread::thread<void (*)(int), int>(void (*)(int), int, boost::disable_if<boost::thread_detail::is_convertible<void (*&)(int), boost::thread_attributes>, boost::thread::dummy*>::type) /usr/local/include/boost/thread/detail/thread.hpp:401 (a+0x40c163)
#3 main /tmp/1550569011.2128205/main.cpp:155 (a+0x40685a)
Thread T2 (tid=23641, running) created by main thread at:
#0 pthread_create ../../.././libsanitizer/tsan/tsan_interceptors.cc:915 (libtsan.so.0+0x2af6b)
#1 boost::thread::start_thread_noexcept() <null> (libboost_thread.so.1.66.0+0x11879)
#2 boost::thread::thread<void (*)(int), int>(void (*)(int), int, boost::disable_if<boost::thread_detail::is_convertible<void (*&)(int), boost::thread_attributes>, boost::thread::dummy*>::type) /usr/local/include/boost/thread/detail/thread.hpp:401 (a+0x40c163)
#3 main /tmp/1550569011.2128205/main.cpp:156 (a+0x40687a)
SUMMARY: ThreadSanitizer: data race /root/orig/gcc-8.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/streambuf:458 in std::basic_streambuf<char, std::char_traits<char> >::sputn(char const*, long)
==================
- я изменил код итератора, предложенный BJ
- Я включил r /w код для будущей ссылки, предложенной в комментарии.