2011-06-10 57 views
3

事情似乎有效,但我不确定这是否是最好的方式。从另一个线程修改向量中的指针数据是否安全?

基本上我有一个异步检索数据的对象。该对象具有在主线程上分配和解除分配的指针向量。使用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); 
     } 
+0

如果您希望得到明智的答案,您需要添加更多信息。目前很重要的一点是缺失,特别是'SomeMethod','IsComplete'是什么以及如何实现以及* complete *标志如何产生。如果处理函数的最后一行是一个赋值,除非这是用户定义的类型,并且它被锁定,并且它是将“IsComplete”设置为true的那个,否则答案是否定的,这是不安全的。但我的猜测是,这个处理函数只是一个骨架。 – 2011-06-10 23:03:15

+0

是啊,这是一个很大的框架,我只是想表明它调用方法并在其中指定成员。在这一点上,我认为只要每个指针的每个回调只有一个线程的语义保持不变,我就会好起来的。增量异步检索引入了太多的延迟,所以我并不担心该实现。 – AJG85 2011-06-10 23:08:59

+0

这种方法本身并没有什么错,但魔鬼在细节中。根据实际操作是什么以及如何对* shared *数据执行同步(我的猜测是共享数据只是一个由'IsComplete'检查的标志),那么它可能是正确的。无论是最好的方法还是可以简化的方法都是一个不同的问题,但没有上下文就无法说清楚。可能很重要的事情是:'SmartPtr'线程安全的实现?那里的竞赛条件很有可能。 – 2011-06-11 10:10:37

回答

2

不,不安全。

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

+0

每个检索只触发一个回调。所以在任何时候只有一个线程访问指针。如果你提出了5个请求,它会分配5个对象,5个绑定,启动它们,另外5个线程的5个回调将被调用来执行肮脏的工作,然后在下次你提出更多请求时清理事件完成。 – AJG85 2011-06-10 20:47:19

+0

“完成”由回调触发确定,完成它的处理,然后在执行结束时触发完整的回调。 – AJG85 2011-06-10 21:11:15

+0

啊,在回调中完成的任务类型“暗示”您使用'DataObject'来传递_another_结构,以便回调方法可以处理它。你会用更多关于回调方法的语义的更多细节来更新问题,即它在它的工作方式上是什么,它是在线程之间共享的吗? – 2011-06-11 02:59:21

3

现在看来,Cleanup代码可能会销毁一个对象,其中的ProcessResults回调处于运行状态。当您在回调中删除指针时会导致问题。

我的建议是您扩展m_dataMutex的语义涵盖回调,但如果回调是长期运行,或可在SetupRetrieve内嵌发生(有时会发生这种情况 - 虽然在这里你的状态回调是一个不同的线程,在这种情况下你可以)然后事情就更复杂了。目前m_dataMutex对于是否控制对矢量或其内容的访问有点困惑。在明确其范围后,ProcessResults可以被增强以验证锁中有​​效载荷的有效性。

+0

这就是IsComplete方法检查没有担心那里...我想锁定在回调中,但是这些在任意时间被任意线程调用,因为服务器完成异步任务。锁定在一个互斥锁上同步,从而形成一个瓶颈,而目前的回调可以并行完成。 – AJG85 2011-06-10 20:40:03

+0

@ AJG85 - 因为你的回调得到了一个原始指针,所以在使用它之前,你必须确认它仍然有效。你能否将智能指针转换为ref-counting类型,并且只锁定向量来检查它是否正常并获得对该对象的安全引用?然后,大部分回调逻辑仍然可以并行进行。另一个问题是,回调中对象的操作是否会干扰其他线程的逻辑,如果是这样的话,这又是一个问题。 – 2011-06-10 20:41:47

+0

我遗漏了空检查和大部分看起来不相关的代码。此外,“SmartPtr”的名称不同,但它是一个与STL容器兼容的引用计数对象(可能在某一天有利于C++ 0x)...有一个弱指针的朋友类I可能可以用来获取引用,但变化的生命需要超出回调和线程的范围。也没有别的东西可以看到或使用私人对象,直到处理结果回调,当它触发完成回调。 – AJG85 2011-06-10 21:07:46

0

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

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

+0

它不会更新传递给它的数据结构上的指针“ProcessResults”_operates_。 – 2011-06-10 20:32:55

+0

@Khaled:这是一个指针,但它确实调用方法并更改指针指向的对象的成员。 – AJG85 2011-06-10 20:41:07

+0

所以我需要使用一个原子指针?我不需要交换或更改指针指向的内容就可以使用它。此外,其他线程不会删除任何内容,因为释放在另一个线程上创建的内容听起来像一周中的任何一天的坏消息。 – AJG85 2011-06-10 20:44:13

相关问题