2011-06-10 2 views
3

일이 잘되는 것처럼 보이지만 이것이 최선의 방법인지 확신 할 수 없습니다.다른 스레드에서 벡터의 포인터 데이터를 수정하는 것이 안전합니까?

기본적으로 데이터의 비동기 검색을 수행하는 개체가 있습니다. 이 객체에는 주 스레드에서 할당되고 할당 해제 된 포인터 벡터가 있습니다. 부스트 함수를 사용하면 프로세스 결과 콜백이이 벡터의 포인터 중 하나와 바인딩됩니다. 실행되면 임의의 스레드에서 실행되고 포인터의 데이터가 수정됩니다.

이제 비동기 검색 개체가 더 많은 요청을받는 경우 벡터에서 지우고 지우는 부분 주위에 중요한 섹션이 있습니다. 그러나 포인터를 수정하는 콜백에서 어떤 종류의 가드가 필요한지 궁금합니다. 데이터도 마찬가지입니다. 그것을 이것은 ProccessResults 콜백에 대한 실제 코드 (플러스 이익을 위해 댓글)

void ProcessResults(CRetrieveResults* pRetrieveResults, CRetData* data) 
     { 
// pRetrieveResults is delayed binding that server passes in when invoking callback in thread pool 
// data is raw pointer to ref counted object in vector of main thread (the DataObject* in question) 

       // if there was an error set the code on the atomic int in object 
      data->m_nErrorCode.Store_Release(pRetrieveResults->GetErrorCode()); 

       // generic iterator of results bindings for generic sotrage class item 
      TPackedDataIterator<GenItem::CBind> dataItr(&pRetrieveResults->m_DataIter); 
       // namespace function which will iterate results and initialize generic storage 
      GenericStorage::InitializeItems<GenItem>(&data->m_items, dataItr, pRetrieveResults->m_nTotalResultsFound); // this is potentially time consuming depending on the amount of results and amount of columns that were bound in storage class definition (i.e.about 8 seconds for a million equipment items in release) 
       // atomic uint32_t that is incremented when kicking off async retrieve 
      m_nStarted.Decrement(); // this one is done processing 

       // boost function completion callback bound to interface that requested results 
      data->m_complete(data->m_items); 
     } 
+0

당신이 현명한 대답을 원한다면 더 많은 정보를 추가해야합니다. 현재 중요한 것, 특히 SomeMethod와 IsComplete가 구현되고 어떻게 * complete * 플래그가 올라 갔는지, 그리고 무엇이 빠졌는가? 처리 함수의 마지막 줄이 할당이고, 그것이 사용자 정의 유형이 아니고 잠그지 않고'IsComplete'를 true로 설정하면, 대답은 '아니오'입니다. 안전하지 않습니다. 하지만 제 생각에 그 처리 기능은 단순한 해골에 지나지 않습니다. –

+0

네, 이것은 골격입니다. 저는 그냥 메서드를 호출하고 그 안에 멤버를 할당한다는 것을 보여주고 싶었습니다. 이 시점에서 포인터 당 하나의 콜백 당 하나의 스레드 만 갖는 의미가 유지되는 한 괜찮을 것이라고 생각합니다. 점진적 비동기 검색은 너무 많은 대기 시간을 가져 오므로 아직 구현이 걱정되지 않습니다. – AJG85

+0

본질적으로 접근 방법에는 문제가 없지만 악마는 세부 사항에 있습니다.실제 작업이 무엇이고 동기화가 * 공유 * 데이터에서 수행되는 방식에 따라 (내 생각에 공유 데이터는 IsComplete에 의해 검사 된 플래그 일뿐입니다) 올바른 것일 수 있습니다. 최선의 접근 방식이든 단순화 될 수 있는지는 전혀 다른 문제이지만, 맥락 없이는 말할 수 없습니다. 중요성이있는 것들 :'SmartPtr' 쓰레드를 안전하게 구현 하는가? 경쟁 조건에 대한 가능성이 높습니다. –

답변

2

번호 :

class CAsyncRetriever 
{ 
    // typedefs of boost functions 

    class DataObject 
    { 
     // methods and members 
    }; 

public: 
    // Start single asynch retrieve with completion callback 
    void Start(SomeArgs) 
    { 
     SetupRetrieve(SomeArgs); 
     LaunchRetrieves(); 
    } 

protected: 
    void SetupRetrieve(SomeArgs) 
    { 
      // ... 

     { // scope for data lock 
      boost::lock_guard<boost::mutex> lock(m_dataMutex); 
      m_inProgress.push_back(SmartPtr<DataObject>(new DataObject))); 
      m_callback = boost::bind(&CAsyncRetriever::ProcessResults, this, _1, m_inProgress.back()); 
     } 

      // ... 
    } 

    void ProcessResults(DataObject* data) 
    { 
       // CALLED ON ANOTHER THREAD ... IS THIS SAFE? 
     data->m_SomeMember.SomeMethod(); 
       data->m_SomeOtherMember = SomeStuff; 
    } 

    void Cleanup() 
    { 
       // ... 

     { // scope for data lock 
      boost::lock_guard<boost::mutex> lock(m_dataMutex); 
      while(!m_inProgress.empty() && m_inProgress.front()->IsComplete()) 
       m_inProgress.erase(m_inProgress.begin()); 
     } 

       // ... 
     } 

private: 
    std::vector<SmartPtr<DataObject>> m_inProgress; 
    boost::mutex m_dataMutex; 
     // other members 
}; 

편집 :

는 희망이 몸매는 여전 하구나 의사 코드는 일을 더 명확하게 안전하지 않습니다.

ProcessResultsDataObject을 통해 전달 된 데이터 구조에서 작동합니다. 이는 서로 다른 스레드간에 상태가 공유되었음을 나타내고, 두 스레드가 동시에 데이터 구조에서 작동하면 병목 현상이 발생할 수 있습니다.

+0

검색 당 하나의 콜백 만 실행됩니다. 따라서 언제든지 하나의 스레드 만 포인터에 액세스합니다. 5 개의 요청을하면 5 개의 객체를 할당하고 5 개의 바인딩을 만들고 5 개의 다른 스레드에서 5 개의 콜백을 호출하여 더러운 작업을 수행 한 다음 나중에 다음에 더 많은 요청을 할 때 정리됩니다. 완료되었습니다. – AJG85

+0

또한 "doneness"는 콜백 실행에 의해 결정되고, 처리가 완료되며, 실행이 끝나면 전체 콜백을 실행합니다. – AJG85

+0

아, 콜백 메서드에서 수행 할 수 있도록 _another_ 구조체를 전달하기 위해 DataObject를 사용하는 콜백 종류에서 수행 된 할당이 "함축적"입니다. 콜백 메소드의 의미에 대해 조금 더 자세하게 질문을 업데이트 하시겠습니까? 즉, 스레드가 작동하는 방식과 스레드간에 공유되는 방식입니다. –

3

약자로 Cleanup 코드는 ProcessResults에 대한 콜백이 수행되는 개체를 파괴 할 수 있습니다. 콜백에서 포인터를 deref 할 때 문제가 발생할 것입니다.

나의 제안은 콜백 장기 실행 경우지만, 콜백을 포함하는 당신의 m_dataMutex의 의미를 확장, 또는 때때로 이런 일이 않습니다 (SetupRetrieve 내에서 인라인 일어날 수 있다는 것이다 - 당신이 상태 비록 여기 콜백이 켜져 다른 스레드,이 경우에는 당신이 좋아) 다음 것들이 더 복잡합니다. 현재 m_dataMutex은 벡터 또는 해당 내용에 대한 액세스를 제어하는지 아니면 둘 다에 대한 액세스를 제어하는지 조금 혼란 스럽습니다. 범위가 명확 해지면 ProcessResults을 확장하여 잠금 내에서 페이로드의 유효성을 확인할 수 있습니다.

+0

IsComplete 메서드는 걱정하지 않습니다. 콜백에서 잠금을 생각했지만 서버가 비동기 작업을 완료 할 때 임의의 시간에 호출합니다. 단일 뮤텍스를 잠그면 병목 현상이 생기고 콜백은 동시에 수행 될 수 있습니다. – AJG85

+0

@ AJG85 - 콜백이 원시 포인터를 얻으므로, 사용하기 전에 반드시 유효해야합니다. 스마트 포인터를 참조 계산 유형으로 변환하고 벡터를 잠궈 OK인지 확인하고 객체에 대한 보안 참조를 얻을 수 있습니까? 그러면 콜백 논리의 대부분은 여전히 ​​병렬로 진행될 수 있습니다. 다른 문제는 콜백 객체의 조작이 다른 스레드의 논리와 간섭하는지 여부입니다. 그렇다면 다시 문제가됩니다. –

+0

null 체크와 관련없는 것으로 보인 코드의 대부분을 생략했습니다. 또한 "SmartPtr"은 다른 이름으로 사용되지만 STL 컨테이너와 호환되는 참조 계산 된 객체입니다 (언젠가 C++ 0x를 선호 할 수 있습니다). 약한 포인터 친구 클래스가 있습니다. 참조를 얻기 위해 사용할 수 있지만 변경 사항의 수명은 콜백 및 스레드의 범위를 넘어서야합니다. 또한 완료 콜백을 발생시킬 때 프로세스 결과 콜백이 끝날 때까지는 사적인 객체를 보거나 사용할 수 없습니다. – AJG85

0

포인터를 업데이트하는 것은 원자 적 조작이어야하지만 InterlockedExchangePointer (Windows)을 사용하면됩니다. 리눅스에 상응하는 것이 무엇인지 모릅니다.

한 스레드가 사용되지 않는 포인터를 사용하는 경우에만 고려해야합니다. 다른 스레드가 원래 포인터가 가리키는 객체를 삭제합니까? 그렇다면 명확한 문제가 있습니다.

+0

전달 된 데이터 구조에서 'ProcessResults' _operates_ 포인터를 업데이트하지 않습니다. –

+0

@Khaled : 그것은 포인터이지만 포인터를 가리키는 객체의 메서드와 호출 멤버를 호출합니다. – AJG85

+0

그래서 원자 포인터를 사용해야합니까? 포인터를 가리키는 포인터를 바꾸거나 바꿀 필요가 없습니다. 또한 다른 스레드는 다른 스레드에서 생성 된 것을 할당 해제하는 것이 어떤 요일에도 나쁜 소식처럼 들리므로 아무 것도 삭제하지 않습니다. – AJG85

관련 문제