我正在尝试实现一个多线程框架,其中输出对象是在我的网络线程运行的每个帧的末尾创建的,这样另一个线程可以在其帧的开头获取最新的“已完成”输出“指针并知道它对输出对象中存储的任何数据具有安全且完整的只读访问权。
我(非常早)的方法主要涉及以下代码块:
NetworkHandler -
void NetworkHandler::Tick()
{
// Tick on our io_service
m_ios.poll();
// Assemble new output data
this->AssembleOutput();
}
ClientNetworkHandler -
void ClientNetworkHandler::AssembleOutput()
{
std::tr1::shared_ptr<crusade::task::TaskOutput> newOutput(new crusade::task::TaskOutput());
newOutput->m_outputElements["connected"] = std::tr1::shared_ptr<crusade::task::TaskOutputElement>(new NetworkConnectedTaskOutputElement(this->m_isConnected));
this->m_latestOutput.swap(newOutput);
}
PyCruHandler -
void PyCruHandler::Tick()
{
printf("PyCruHandler\n");
// Get any necessary inputs from other threads
m_latestNetworkOutput.swap(crusade::task::THManager::GetInstance()->GetTaskHandler(crusade::task::THManager::TH_NETWORK)->GetLatestOutput());
// Other unrelated processing to go here
}
基本上,ClientNetworkHandler和PyCruHandler在不同的线程上独立运行。 PyCruHandler实际上从未对其m_latestNetworkOutput副本做任何事情;我已经注释掉了以任何方式访问它的所有其他实例,我仍然有以下问题:
如果我允许两个线程调用swap(或者operator =等价物),那么最终(通常在运行2秒内,但有时需要几分钟)我将在operator new或allocator上得到以下错误删除某种:
“HEAP:自由堆块2bab3b0在2bab3dc被修复后被修改 Windows触发了一个断点。
这可能是由于堆的损坏,这表明存在错误......等等。“
我只是一个新手,但对我而言,这似乎表明了shared_ptr对象之间的线程安全和时序敏感访问问题。但是,我一直对conf_ptr线程安全的细微差别的解释(在这里和其他地方)感到困惑 - 一条读取表明引用计数是线程安全的,因此复制shared_ptrs是安全的(但是它们的内部对象不会是线程安全的),其他阅读文件表明在shared_ptr中实际上没有任何有用的线程安全性。我已经阅读了关于shared_ptrs的线程安全性的boost文档,但是我仍然没有在脑海中澄清这是否应该是我的代码中的问题。
我的问题是,这里的任何人都可以发现我正在做的任何明显的缺陷吗?我的目标是我可以访问仍然由拥有线程存储的最新输出对象,然后该对象将不被删除(即使在拥有线程移动到以后的输出之后),直到输出的每个用户完成它同样。在我的脑海里,共享指针看起来很完美......但是经过3个小时的撞击我之后,我开始怀疑......
非常感谢您提前,如果我以某种方式错误地发布,我会道歉;这是我第一次来到这里,就协议而言,FAQ似乎相当悠闲!
答案 0 :(得分:4)
这里最好的资源可能是the documentation:
线程安全
shared_ptr对象提供相同的功能 内置的线程安全级别 类型。 shared_ptr实例可以是 “read”(仅使用const访问) 操作)同时多个 线程。不同的shared_ptr 实例可以“写入” (使用可变操作访问 例如operator =或reset) 同时由多个线程 (即使这些实例是副本, 并分享相同的引用计数 的下面。)
示例:
shared_ptr<int> p(new int(42));
//--- Example 1 ---
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
//--- Example 2 ---
// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
//--- Example 3 ---
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
//--- Example 4 ---
// thread A
p3 = p2; // reads p2, writes p3
// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"
//--- Example 5 ---
// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
我将假设代码中的m_latestOutput
为shared_ptr
- 在这种情况下,您最接近的示例类似于数字5(多次写入)。
答案 1 :(得分:1)
根据文档,您必须同步读取和写入同步。交换是:)。考虑使用operator =()代替,那只是写。
此外,你的客户端线程必须复制m_latestNetworkOutput(可以读取),如果他们想让对象保持活着,直到它们完成(我认为这是你在PyCruHandler :: Tick()中所做的)。
无论如何,你必须同步写:
this->m_latestOutput.swap(newOutput);
并阅读:
m_latestNetworkOutput.swap(crusade::task::THManager::GetInstance()->GetTaskHandler(crusade::task::THManager::TH_NETWORK)->GetLatestOutput());
将交换更改为分配 - 交换后不需要旧指针,是吗?