【发布时间】:2011-06-10 20:20:32
【问题描述】:
似乎一切正常,但我不确定这是否是最好的方法。
基本上我有一个异步检索数据的对象。这个对象有一个指针向量,这些指针在主线程上分配和取消分配。使用 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 回调的实际代码(加上 cmets 为您带来好处)
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);
}
【问题讨论】:
-
如果您期望得到合理的答案,您需要添加更多信息。目前缺少重要的部分,特别是
SomeMethod、IsComplete是什么以及如何实现以及如何引发 complete 标志。如果处理函数的最后一行是赋值,除非是用户定义类型,并且它被锁定,并且将IsComplete设置为true,答案是否定的,它不安全。但我的猜测是,那个处理函数只是一个骨架。 -
是的,这是一个骨架,我只是想展示它调用方法并在其中分配成员。在这一点上,我想我会没事的,只要每个指针每个回调只有一个线程的语义仍然存在。增量异步检索引入了太多延迟,所以我还不担心这种实现。
-
这种方法本质上没有错,但魔鬼在细节中。根据实际操作是什么以及如何对 shared 数据执行同步(我的猜测是共享数据只是
IsComplete检查的标志),那么它可能是正确的。它是最好的方法还是可以简化是一个不同的问题,但如果没有上下文,就无法判断。可能很重要的事情:SmartPtr线程的实现是否安全?那里很有可能出现竞争状况。 -
顺便说一句,
ProcessResults应该是static,如果不是static,你是共享>this指针,然后你还必须添加为每个成员对象锁定ProcessResults。 -
你可能是对的......但是我想我会停止在完成回调中传递异步检索器,因为它并不是真正需要的,然后唯一使用的其他成员是原子的,所以这将是很好。
标签: c++ boost stl thread-safety