Мне нужно реализовать огромную хеш-таблицу, которая поддерживает несколько потоков для вставки и получения в одно и то же время. Клавиши ИНТ и вторые элементы являются векторами объекта Т.TBB одновременная неупорядоченная карта вызывает segfault
class T {
//class definitions here
}
В настоящее время реализация помогла с TBB :: concurrent_unordered_map. По документации, похоже, разрешено вставлять и обходить в одно и то же время. Тем не менее, работа с несколькими pthreads приведет к ошибке сегментации, хотя последовательный тест совершенно прекрасен. Обратите внимание, что нет никаких операций удаления или удаления, т. Е. Хеш-таблица разрешается только расти.
std::vector<T*> get(int key) {
//Note that the hash table hashTable is shared by multiple threads
tbb::concurrent_unordered_map<int, std::vector<T*>>::iterator it = hashTable.find(key);
if (it != hashTable.end())
return it->second;
else {
std::vector<T*> newvector;
return newvector;
}
}
void insert(int key, T *t) {
tbb::concurrent_unordered_map<int, std::vector<T*>>::iterator it = hashTable.find(key);
if (it != hashTable.end())
it->second.push_back(t);
else {
std::vector<T*> newTs;
newTs.push_back(t);
hashTable.insert(it, makepair(key, newTs));
}
}
Для отладки, что случилось, я первый изменил зЬй :: вектор к TBB :: concurrent_vector, а затем изменить функцию Get() и вставить() следующим образом.
std::vector<T*> get_test(int key) {
std::vector<T*> test;
//Note that the hash table hashTable is shared by multiple threads
tbb::concurrent_unordered_map<int, tbb::concurrent_vector<T*>>::iterator it = hashTable.find(key);
if (it != hashTable.end())
test.insert(test.end(), it->second.begin(), it->second.end());
for (T* _t : test)
if (!_t)
printf("Bug happens here!\n"); //Segfault is originated here because a NULL is returned in the vector
return test;
}
void insert_test(int key, T *t) {
//Here t is guaranteed to be not NULL
if(!t)
std::terminate();
tbb::concurrent_unordered_map<int, tbb::concurrent_vector<T*>>::iterator it = hashTable.find(key);
if (it != hashTable.end())
it->second.push_back(t);
else {
std::vector<T*> newTs;
newTs.push_back(t);
hashTable.insert(it, makepair(key, newTs));
}
}
Теперь мы можем видеть причину, что параллельная программа падает некоторая указатель NULL возвращается в функции get_test(). Напомним, что в функции insert_test() NULL никогда не вставлен в качестве вторых элементов.
Следующие вопросы задавать.
(1) Я где-то читал, что одновременная вставка в tbb :: concurrent_unordered_map может привести к сбою некоторых попыток вставки, а затем темповые объекты будут уничтожены. Это причина, по которой NULL наблюдается в функции get_test()?
(2) Может ли TBB действительно разрешить вставку и обход в одно и то же время? Это означает, что в то время как некоторые потоки вставляются, другие могут одновременно вызвать get().
(3) Есть ли лучшая реализация, т. Е. Улучшенная параллельная хеш-таблица, поддерживающая параллельную вставку и чтение?
Как @for_stack предложил, я проверил вторые элементы concurrent_vectors и вся программа является работоспособной. Дальнейшее испытание проводят следующим образом:
tbb::concurrent_vector<T*> get_test(int key) {
tbb::concurrent_vector<T*> test;
//Note that the hash table hashTable is shared by multiple threads
tbb::concurrent_unordered_map<int, tbb::concurrent_vector<T*>>::iterator it = hashTable.find(key);
if (it != hashTable.end())
test = it->second;
int i = 0;
for (T* _t : test)
if (!_t)
i++;
if (i > 0)
printf("%d of %lu Ts are NULL\n", i, test.size()); //Segfault is originated here because a NULL is returned in the vector
return test;
}
void insert_test(int key, T *t) {
//Here t is guaranteed to be not NULL
if(!t)
std::terminate();
tbb::concurrent_unordered_map<int, tbb::concurrent_vector<T*>>::iterator it = hashTable.find(key);
if (it != hashTable.end())
it->second.push_back(t);
else {
tbb::concurrent_vector<T*> newTs;
newTs.push_back(t);
hashTable.insert(it, make_pair(key, newTs));
}
}
Выход
1 of 2 Ts are NULL
Это означает, что не все объекты (Т) возвращаются в GET() равны нулю.
Опять же последовательный (или даже 1 поток) работает нормально.
Цените свое объяснение. Раньше я думал, что TBB автоматически применит блокировку для одновременной вставки в тот же ключ. Однако вышеприведенный код больше похож на псевдокод, и в реальной реализации второй элемент действительно является tbb :: concurrent_vector, и вся программа сработает после более длительного времени (чем при использовании std :: vector). Причина указана выше, поскольку некоторые указатели NULL возвращаются в get(), и я не думаю, что изменение возвращаемого типа get() на concurrent_vector даже поможет. Возможно, мне следует отредактировать код, чтобы не вызвать путаницу. – defg