2011-12-26 3 views
1

concurrent_unordered_map이 있습니다. 나는 insert 함수 (그리고 다른 함수)를 사용하여지도에 동시에 삽입하려고합니다. 그러나 여러 번이 기능은 내부의 insert 내부에서 심하게 손상됩니다. 다음은 몇 가지 코드입니다.concurrent_unordered_map을 사용하여 충돌이 발생했습니다.

class ModuleBase { 
public: 
    virtual Wide::Parser::AST* GetAST() = 0; 
    virtual ~ModuleBase() {} 
}; 
struct ModuleContents { 
    ModuleContents() {} 
    ModuleContents(ModuleContents&& other) 
     : access(other.access) 
     , base(std::move(other.base)) {} 
    Accessibility access; 
    std::unique_ptr<ModuleBase> base; 
}; 
class Module : public ModuleBase { 
public: 
    // Follows Single Static Assignment form. Once it's been written, do not write again. 
    Concurrency::samples::concurrent_unordered_map<Unicode::String, ModuleContents> contents; 
    Wide::Parser::AST* GetAST() { return AST; } 
    Wide::Parser::NamespaceAST* AST; 
}; 

이것은 실제로지도에 삽입하는 데 사용하는 기능입니다. 더 많은 것이 있지만지도에 손대지 않으며 반환 값인 insert 만 사용합니다.

void CollateModule(Parser::NamespaceAST* module, Module& root, Accessibility access_level) { 
// Build the new module, then try to insert it. If it comes back as existing, then we discard. Else, it was inserted and we can process. 
Module* new_module = nullptr; 
ModuleContents m; 
{ 
    if (module->dynamic) { 
     auto dyn_mod = MakeUnique<DynamicModule>(); 
     dyn_mod->libname = module->libname->contents; 
     new_module = dyn_mod.get(); 
     m.base = std::move(dyn_mod);  
    } else { 
     auto mod = MakeUnique<Module>(); 
     new_module = mod.get(); 
     m.base = std::move(mod); 
    } 
    new_module->AST = module; 
    m.access = access_level; 
} 
auto result = root.contents.insert(std::make_pair(module->name->name, std::move(m))); 

이것은 루트 함수입니다. 다른 입력에서 많은 스레드로부터 병렬로 호출되지만, 동일한 root을 사용합니다.

void Collater::Context::operator()(Wide::Parser::NamespaceAST* input, Module& root) { 
std::for_each(input->contents.begin(), input->contents.end(), [&](Wide::Parser::AST* ptr) { 
    if (auto mod_ptr = dynamic_cast<Wide::Parser::NamespaceAST*>(ptr)) { 
     CollateModule(mod_ptr, root, Accessibility::Public); 
    } 
}); 
} 

전적으로 잘 모르겠습니다. 공유 상태의 비트가 하나 있으며 원자 적으로 만 액세스 할 수 있습니다. 그렇다면 코드가 왜 죽어 가고 있습니까?

편집 : 이것은 실제로 내 자신의 잘못입니다. 충돌은 insert 줄에 있었는데, 나는 문제라고 생각했지만 - 이 아니 었습니다. 동시성과 관련이 전혀 없습니다. 나는 result잘못된 방향으로 반환 값을 테스트 주위 - 즉, true 표준이 insertion succeeded에 대한 true 정의하는 반면, value did not exist에 대한 false, value existed에 - 즉, value did not exist을. 이것은 메모리 관리를 크게 망가뜨려 충돌을 일으켰습니다. 코드가 정확히 어떻게 발생했는지는 알 수 없습니다. 올바른 부정을 삽입하면 완벽하게 작동합니다. 이는 동시 울타리를 점프하기 전에 단일 스레드 버전을 제대로 테스트하지 않았기 때문에 나타났습니다.

답변

1

한 가지 가능성은 이동 의미론의 일부 문제로 인해 충돌하는 것입니다. NULL 포인터가 역 참조 (dereference)로 인한 충돌입니까? 이동 한 후 의도하지 않게 개체 (예 : ModuleContents)에 액세스 한 경우 발생합니다.

충돌이 동시성 버그의 결과 일 수 있습니다. concurrent_unordered_map은 삽입 및 검색이 원자 적이라는 점에서 스레드로부터 안전합니다. 그러나 내부에 저장되어있는 것은 자동으로 보호되지 않습니다. 따라서 여러 스레드가 동일한 ModuleContents 개체를 검색하는 경우 Module 안에있는 AST 트리를 공유합니다. const 포인터 나 참조를 볼 수 없기 때문에 어떤 참조가 수정 가능한지 잘 모르겠습니다. 공유되고 수정 가능한 것은 동기화 메커니즘 (예 : 잠금)에 의해 보호되어야합니다.

+0

const-correctness가 부족하다는 점에 관해서는 좋은 생각입니다. 그러나'concurrent_unordered_map '만 실제로 * 변경할 수 있습니다 *. – Puppy

+0

사실이라고 생각하는 모든 const 한정자를 추가하고 컴파일하는지 확인하지 않는 이유는 무엇입니까? 또한 해당 const 개체에 대한 변경 가능한 참조가 없는지 확인하십시오. –

+0

나는 내 가정을 실제로 검증하기 위해 그 일을했다. 그러나, 나는 실제 문제를 발견했다. 솔루션에 대한 편집을 참조하십시오. – Puppy

관련 문제