C++ 从另一个线程修改向量中指针的数据安全吗?

C++ 从另一个线程修改向量中指针的数据安全吗?,c++,boost,stl,thread-safety,C++,Boost,Stl,Thread Safety,事情似乎正在进行,但我不确定这是否是最好的解决办法 基本上我有一个对象,它可以异步检索数据。这个对象有一个指针向量,这些指针在主线程上分配和取消分配。使用boost函数,流程结果回调与此向量中的一个指针绑定。当它触发时,它将在某个任意线程上运行,并修改指针的数据 现在,我有一些关键的部分,这些部分被推到向量中,并在异步检索对象接收到更多请求时进行擦除,但我想知道在回调中是否也需要某种保护来修改指针数据 希望这个精简的伪代码能让事情变得更清楚: class CAsyncRetriever {

事情似乎正在进行,但我不确定这是否是最好的解决办法

基本上我有一个对象,它可以异步检索数据。这个对象有一个指针向量,这些指针在主线程上分配和取消分配。使用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
};
CAsyncRetriever类
{
//boost函数的typedef
类数据对象
{
//方法和成员
};
公众:
//使用完成回调启动单个异步检索
无效开始(某些参数)
{
设置检索(SomeArgs);
启动();
}
受保护的:
无效设置检索(某些参数)
{
// ...
{//数据锁的作用域
boost::lock\u guard lock(m\u dataMutex);
m_inProgress.push_back(SmartPtr(新数据对象));
m_callback=boost::bind(&CAsyncRetriever::ProcessResults,this,_1,m_inProgress.back());
}
// ...
}
无效处理结果(数据对象*数据)
{
//调用另一个线程…这安全吗?
data->m_SomeMember.SomeMethod();
data->m_SomeOtherMember=SomeStuff;
}
空洞清理()
{
// ...
{//数据锁的作用域
boost::lock\u guard lock(m\u dataMutex);
而(!m_inProgress.empty()&&m_inProgress.front()->IsComplete())
m_inProgress.erase(m_inProgress.begin());
}
// ...
}
私人:
std::向量m_进程;
mutex m_dataMutex;
//其他成员
};
编辑:这是ProcessResults回调的实际代码(加上您的注释)

void处理结果(CRetrieveResults*pRetrieveResults,CRetData*data)
{
//pRetrieveResults是服务器在线程池中调用回调时传入的延迟绑定
//数据是指向主线程向量中ref counted对象的原始指针(有问题的DataObject*)
//如果有错误,请在对象中的原子int上设置代码
data->m_nErrorCode.Store_Release(pRetrieveResults->GetErrorCode());
//泛型sotrage类项的结果绑定的泛型迭代器
tPackagedDataiter数据编辑器(&pRetrieveResults->m_数据编辑器);
//命名空间函数,该函数将迭代结果并初始化通用存储
GenericStorage::InitializeItems(&data->m_items,dataItr,pRetrieveResults->m_nTotalResultsFound);//这可能会耗费大量的时间,具体取决于存储类定义中绑定的结果和列的数量(例如,对于发布中的一百万个设备项,大约需要8秒)
//启动异步检索时递增的原子uint32\t
m_nStarted.Decrement();//这一个已完成处理
//绑定到请求结果的接口的boost函数完成回调
数据->m_完成(数据->m_项目);
}
不,它不安全


ProcessResults
对通过
DataObject
传递给它的数据结构进行操作。它表示您在不同的线程之间共享了状态,如果两个线程同时在数据结构上运行,您可能会遇到一些问题。

目前看来,
清理
代码可能会破坏一个对象,该对象正在执行对
处理结果
的回调。当您在回调中取消引用指针时,这将导致问题


我的建议是扩展
m_dataMutex
的语义以包含回调,尽管回调是长时间运行的,或者可以在
SetupRetrieve
中内联发生(有时确实会发生这种情况-尽管在这里您声明回调在不同的线程上,在这种情况下您可以)那么事情就更复杂了。目前,
m_dataMutex
对于它是控制对向量的访问,还是控制其内容,或者两者都控制,有点困惑。在明确其范围后,
ProcessResults
可以得到增强,以验证锁内有效负载的有效性。

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


唯一需要考虑的是一个线程是否使用了过时的指针。另一个线程是否删除原始指针指向的对象?如果是这样,您肯定有问题。

它不是在更新指针,
ProcessResults
对传递给它的数据结构进行操作。@Khaled:它是指针,但它调用方法并更改指针指向的对象的成员。所以我需要使用原子指针吗?我不需要交换或更改指针指向的内容,只需使用它即可。另外,另一个线程不会删除任何内容,因为取消分配在另一个线程上创建的内容在一周中的任何一天听起来都像是坏消息。@AJG85:我的意思是:“问题不在于更新指针”,看看我之前的评论,我承认这很混乱。无论如何,真正的问题似乎在回调函数中。这就是IsComplete方法检查的内容,不用担心。。。我曾想过锁定回调,但当服务器完成异步任务时,这些回调会在任意时间被任意线程调用。锁定单个互斥锁会进行同步,从而造成瓶颈
    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);
        }