Безопасно ли изменять данные указателя в векторе из другого потока? - PullRequest
3 голосов
/ 11 июня 2011

Кажется, все работает, но я не уверен, что это лучший способ сделать это.

В основном у меня есть объект, который выполняет асинхронный поиск данных. Этот объект имеет вектор указателей, которые выделяются и освобождаются в основном потоке. Используя функции повышения, обратный вызов результатов процесса связан с одним из указателей в этом векторе. Когда он запускается, он работает в каком-то произвольном потоке и изменяет данные указателя.

Теперь у меня есть критические разделы вокруг частей, которые выдвигаются в вектор и стираются в случае, если асинхронный объект поиска получает больше запросов, но мне интересно, нужна ли мне какая-то защита в обратном вызове, который модифицирует данные указателя а также.

Надеюсь, этот уменьшенный псевдокод прояснит ситуацию:

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
};

Редактировать: это фактический код для обратного вызова 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);
        }

Ответы [ 3 ]

3 голосов
/ 11 июня 2011

Как представляется, код Cleanup может уничтожить объект, для которого выполняется обратный вызов ProcessResults. Это вызовет проблемы, когда вы разыграете указатель в обратном вызове.

Мое предложение заключается в том, чтобы вы расширили семантику вашего m_dataMutex для охвата обратного вызова, хотя, если обратный вызов длительный или может происходить внутри строки в пределах SetupRetrieve (иногда это действительно происходит - хотя здесь вы указываете обратный вызов находится в другом потоке, в этом случае у вас все в порядке), тогда все сложнее. В настоящее время m_dataMutex немного озадачен тем, контролирует ли он доступ к вектору или его содержимому, или и то, и другое. После уточнения области действия ProcessResults может быть улучшено для проверки достоверности полезной нагрузки в замке.

2 голосов
/ 11 июня 2011

Нет, это не безопасно.

ProcessResults работает со структурой данных, переданной ей через DataObject.Это указывает на то, что у вас есть общее состояние между разными потоками, и если оба потока одновременно работают со структурой данных, у вас могут возникнуть некоторые проблемы.

0 голосов
/ 11 июня 2011

Обновление указателя должно быть атомарной операцией, но вы можете использовать InterlockedExchangePointer (в Windows), чтобы быть уверенным.Не уверен, что будет эквивалент Linux.

Единственное соображение будет, если один поток использует устаревший указатель.Удаляет ли другой поток объект, на который указывает исходный указатель?Если это так, у вас есть определенная проблема.

...