为啥我会出现比赛条件?
Posted
技术标签:
【中文标题】为啥我会出现比赛条件?【英文标题】:Why am I getting a race condition?为什么我会出现比赛条件? 【发布时间】:2020-11-04 14:40:11 【问题描述】:我正在尝试将多个 CGAL 网格组合成一个几何体。
我有以下顺序代码可以正常工作:
while (m_toCombine.size() > 1)
auto mesh1 = m_toCombine.front();
m_toCombine.pop_front();
auto mesh2 = m_toCombine.front();
m_toCombine.pop_front();
bool result = CGAL::Polygon_mesh_processing::corefine_and_compute_union(mesh1, mesh2, mesh2);
m_toCombine.push_back(mesh2);
其中m_toCombine
是std::list<Triangle_mesh_exact>
。
Triangle_mesh_exact
是一种 CGAL 网格(三角多面体几何)。但我认为这与问题无关。
不幸的是,这个过程对于我的预期应用来说太慢了,所以我决定使用“分而治之”的概念并以并行方式组合网格:
class Combiner
public:
Combiner(const std::list<Triangle_mesh_exact>& toCombine) :
m_toCombine(toCombine) ;
~Combiner() ;
Triangle_mesh_exact combineMeshes();
void combineMeshes2();
private:
std::mutex m_listMutex, m_threadListMutex;
std::mutex m_eventLock;
std::list<MiniThread> m_threads;
std::list<Triangle_mesh_exact> m_toCombine;
std::condition_variable m_eventSignal;
std::atomic<bool> m_done = false;
//void poll(int threadListIndex);
;
Triangle_mesh_exact Combiner::combineMeshes()
std::unique_lock<std::mutex> uniqueLock(m_eventLock, std::defer_lock);
int runningCount = 0, finishedCount = 0;
int toCombineCount = m_toCombine.size();
bool stillRunning = false;
bool stillCombining = true;
while (stillCombining || stillRunning)
uniqueLock.lock();
//std::lock_guard<std::mutex> lock(m_listMutex);
m_listMutex.lock();
Triangle_mesh_exact mesh1 = std::move(m_toCombine.front());
m_toCombine.pop_front();
toCombineCount--;
Triangle_mesh_exact mesh2 = std::move(m_toCombine.front());
m_toCombine.pop_front();
toCombineCount--;
m_listMutex.unlock();
runningCount++;
auto thread = new std::thread([&, this, mesh1, mesh2]() mutable
//m_listMutex.lock();
CGAL::Polygon_mesh_processing::corefine_and_compute_union(mesh1, mesh2, mesh2);
std::lock_guard<std::mutex> lock(m_listMutex);
m_toCombine.push_back(mesh2);
toCombineCount++;
finishedCount++;
m_eventSignal.notify_one();
//m_listMutex.unlock();
);
thread->detach();
while (toCombineCount < 2 && runningCount != finishedCount)
m_eventSignal.wait(uniqueLock);
stillRunning = runningCount != finishedCount;
stillCombining = toCombineCount >= 2;
uniqueLock.unlock();
return m_toCombine.front();
不幸的是,尽管格外小心,我还是遇到了内存访问冲突或与mesh1
或mesh2
析构函数相关的错误。
我错过了什么吗?
【问题讨论】:
您应该使用std::lock_guard<std::mutex>
而不是在互斥体上手动调用lock
/unlock
。
我担心toCombineCount
、runningCount
、finishedCount
不受锁保护。例如,如果 toCombineCount >= 2
在检查循环条件时会发生什么情况,但到您实际获取锁时,它已经减少了?
@molbdnilo:我想过,但是当m_toCombine.size()
和m_toCombine.front()
被访问时应该没有其他线程在运行。
-fsanitize=thread
是你的朋友。
大家好。我编辑添加 lock_guards 并保护计数变量。认为它们是原子的,因此可以在没有锁的情况下安全使用。
【参考方案1】:
反而使标准库的检查能力变得复杂:
std::reduce - cppreference.com
Triangle_mesh_exact combine(Triangle_mesh_exact& a, Triangle_mesh_exact& b)
auto success = CGAL::Polygon_mesh_processing::corefine_and_compute_union(a, b, b);
if (!success) throw my_combine_exception;
return b;
Triangle_mesh_exact combineAll()
if (m_toCombine.size() == 1) return m_toCombine.front();
if (m_toCombine.empty()) throw std::invalid_argument("");
return std::reduce(std::execution::par,
m_toCombine.begin() + 1, m_toCombine.end(),
m_toCombine.front(), combine);
【讨论】:
它大大减少了崩溃的次数。但我仍然时不时地得到它们...... 顺便说一句,因为我使用的是列表,所以不得不将m_toCombine.begin() + 1
替换为 std::next(m_toCombine.begin(), 1)
。
我没有您的代码,因此无法验证代码是否编译。我也不熟悉用过的图书馆。如果您要更改 std::reduce
的第一个参数(以制作单线程),您是否仍然有此崩溃?你试过在启用消毒剂的情况下运行吗?
我会进一步调查并在今天晚些时候回来。
我正在使用不支持消毒剂的 MSVC 2017。此外,std::execution::seq
确实解决了这个问题。以上是关于为啥我会出现比赛条件?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我会因为 jQuery 而出现“启动应用程序失败”错误?
为啥我会出现此错误:“数组”第 2 行或附近的语法错误:值数组?
为啥我会收到此错误? “气体估算错误,出现以下消息(见下文)。交易 > 执行可能会失败”