我有一个在一个线程中修改的向量,我需要在另一个线程中使用它的内容。由于性能要求,在这些线程之间锁定是不可接受的。由于在向量迭代时迭代将导致崩溃,我想复制向量然后迭代复制。我的问题是,这种方式也会崩溃吗?
struct Data
{
int A;
double B;
bool C;
};
std::vector<Data> DataVec;
void ModifyThreadFunc()
{
// Here the vector is changed, which includes adding and erasing elements
...
}
void ReadThreadFunc()
{
auto temp = DataVec; // Will this crash?
for (auto& data : temp)
{
// Do stuff with the data
...
}
// This definitely can crash
/*for (auto& data : DataVec)
{
// Do stuff with the data
...
}*/
}
vector::operator=
的基本线程安全保证是:
“如果抛出异常,则容器处于有效状态。”
这里有哪些类型的例外?
修改
我使用双缓冲解决了这个问题,并在下面发布了我的答案。
答案 0 :(得分:3)
我的问题是,这种方式也会崩溃吗?
是的,您仍然有数据竞争。如果线程A在线程B创建副本时修改了向量,则向量的所有迭代器都将失效。
这里有哪些类型的例外?
std::vector::operator=(const vector&)
将抛出内存分配失败,或者如果包含的元素抛出副本。同样的事情适用于复制构造,这是代码中标记的行&#34; 这会崩溃吗?&#34;实际上是在做。
这里的根本问题是std::vector
不是线程安全的。您要以使用锁/互斥锁保护它,或者将其替换为线程安全的容器(例如the lock-free containers in Boost.Lockfree或libcds)。
答案 1 :(得分:3)
正如其他答案所指出的那样,你所要求的是不可行的。如果您有并发访问权限,则需要同步,故事结束。
话虽这么说,像你这样的要求同步不是一种选择并不罕见。在这种情况下,你仍然可以做的是摆脱并发访问。例如,您提到在执行游戏循环中每帧访问一次数据。是否严格要求您从当前帧获取数据,还是来自 last 帧的数据?
在这种情况下,您可以使用两个向量,一个由生产者线程写入,另一个由所有使用者线程读取。在框架的最后,您只需交换两个向量。现在,您不再需要*( 1)细粒度同步进行数据访问,因为不再有并发数据访问。
这只是一个如何做到这一点的例子。如果您需要摆脱锁定,请开始考虑如何组织数据访问,以免您首先进入需要同步的情况。
*( 1):严格来说,您仍需要一个同步点,以确保在执行交换时,所有编写器和读取器线程都已完成工作。但是这样做要容易得多(通常在每个帧的末尾都有这样的同步点),并且对性能的影响远小于对每次访问向量的同步。
答案 2 :(得分:0)
我有一个在一个线程中修改的向量,我需要在另一个线程中使用它的内容。由于性能要求,在这些线程之间锁定是不可接受的。
这是不可能满足的要求。
无论如何,两个线程之间的任何数据共享都需要一种锁定,无论是显式还是实现(最终是硬件)。您必须再次检查您的实际要求:暂停一个线程直到另一个线程结束是不可接受的,但您可以锁定短序列指令。和/或可能使用不同的架构。例如,删除向量中的项目是一项代价高昂的操作(线性时间因为您必须将所有数据移动到已删除项目之上),而将其标记为无效则要快得多(恒定时间因为它是一次写入)。如果你真的必须在矢量中间擦除,可能列表更合适。
但是,如果您可以在ReadThreadFunc
中围绕矢量副本以及ModifyThreadFunc
中的任何矢量修改周围进行锁定排除,那么它就足够了。为了给修改线程一个优先级,你可以尝试锁定另一个线程,如果你不能,就立即放弃。
答案 3 :(得分:0)
也许你应该重新考虑你的设计!
每个线程都应该有自己的向量(列表,队列,无论你需要什么)才能工作。所以线程A可以做一些工作并将结果传递给thrad B.当从线程A int线程B的队列中写入数据时,你只需要锁定。
如果没有某种锁定,那是不可能的。
答案 4 :(得分:-3)
所以我使用双缓冲来解决这个问题,它保证不会崩溃,并且读取线程总是会有可用的数据,即使它可能不正确:
house_occupants_occupant