从另一个线程修改向量中的指针数据是否安全?

时间:2011-06-10 20:20:33

标签: c++ boost stl thread-safety

事情似乎有效,但我不确定这是否是最佳方式。

基本上我有一个对象可以异步检索数据。该对象有一个指针向量,在主线程上分配和解除分配。使用boost函数,进程结果回调与此向量中的一个指针绑定。当它触发时,它将在某个任意线程上运行并修改指针的数据。

现在我有关于部分的关键部分正在推入向量和擦除,以防异步检索对象接收到更多请求,但我想知道在回调中是否需要某种保护来修改指针数据同样。

希望这个精简的伪代码能让事情变得更清晰:

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 个答案:

答案 0 :(得分:3)

目前看来,Cleanup代码似乎可以销毁回传到ProcessResults的对象。当您在回调中deref指针时,这将导致问题。

我的建议是你扩展你的m_dataMutex的语义以包含回调,但是如果回调是长时间运行的,或者可能在SetupRetrieve内内联发生(有时这确实会发生 - 尽管在这里你说回调是在不同的线程上,在这种情况下你是好的)然后事情更复杂。目前m_dataMutex对它是否控制对向量或其内容或两者的访问感到有点困惑。澄清其范围后,可以增强ProcessResults以验证锁内有效负载的有效性。

答案 1 :(得分:2)

不,这不安全。

ProcessResults对通过DataObject传递给它的数据结构进行操作。它表示您在不同线程之间具有共享状态,并且如果两个线程同时对数据结构进行操作,则可能会遇到一些麻烦。

答案 2 :(得分:0)

更新指针应该是一个原子操作,但您可以使用InterlockedExchangePointer(在Windows中)来确定。不确定Linux的等价物是什么。

唯一的考虑因素是如果一个线程使用过时的指针。另一个线程是否删除了原始指针指向的对象?如果是这样,你就有了明确的问题。