동시에 삽입하고 가져 오는 다중 스레드를 지원하는 거대한 해시 테이블을 구현해야합니다. 키는 int이고 두 번째 요소는 객체 T의 벡터입니다.TBB 동시 정렬되지 않은 맵으로 인해 segfault가 발생합니다.
class T {
//class definitions here
}
현재 구현에는 tbb :: concurrent_unordered_map이 도움이됩니다. 이 문서에서는 삽입 및 순회를 동시에 허용하는 것으로 보입니다. 그러나 여러 개의 pthread를 실행하면 순차 테스트가 완벽하게 잘되지만 세그멘테이션 오류가 발생합니다. 지우기 또는 팝 작업이 전혀 없습니다. 즉 해시 테이블 만 커질 수 있습니다. 무슨 일이 있었는지 디버깅하려면
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의 동시 삽입으로 인해 일부 삽입 시도가 실패하고 temp 객체가 손상 될 수 있습니다. 이것이 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));
}
}
출력이 모든 개체 (T)가 (이) NULL이다 GET에 반환되지 수단
1 of 2 Ts are NULL
이다.
다시 한 번 순차적으로 (또는 1 스레드로) 실행해도 문제가 없습니다.
설명을 감사드립니다. 나는 TBB가 같은 키에 동시 삽입을 위해 자물쇠를 자동으로 적용한다고 생각했다. 그러나 위의 코드는 의사 코드와 유사 해 보이며 실제 구현에서는 두 번째 요소가 실제로 tbb :: concurrent_vector이고 전체 프로그램이 std :: vector를 사용하는 것보다 오랜 시간이 지나면 충돌합니다. 그 이유는 일부 NULL 포인터가 get()에서 리턴되고, get()의 리턴 타입을 concurrent_vector로 바꾸는 것이 도움이 될 것이라고 생각하지 않기 때문입니다. 혼란을 일으키지 않도록 코드를 편집해야 할 수도 있습니다. – defg