【发布时间】:2022-01-14 09:20:27
【问题描述】:
虽然我知道 QT 声明只有特别声明的类是线程安全的,但我想了解为什么一个“const”标记的方法 - QPainterPath::contains() - 在并行循环中被调用时会中断没有任何并发写操作:
#include <QPainterPath>
#include <omp.h>
#include <iostream>
int main(int argc, char *argv[])
{
QPainterPath path;
path.addRect(-50,-50,100,100);
#pragma omp parallel for
for(int x=0; x<100000; ++x)
if(!path.contains(QPoint(0,0)))
std::cout << "failed\n";
return 0;
}
上面的代码在不应该的时候随机输出“失败”。
我的理解是,尽管方法是“const”,但它正在以某种方式改变其内部状态: https://code.woboq.org/qt5/qtbase/src/gui/painting/qpainterpath.cpp.html#_ZNK12QPainterPath8containsERK7QPointF
我需要比较点是否在来自多个线程的路径内(以加快处理速度),但它不适用于 QPainterPath。即使我为每个线程创建了对象的副本,QT 也会在写入时复制,除非我更改派生对象(强制它分离),否则结果仍然是相同的错误行为,因为它仍然使用相同的共享数据。如果没有这种丑陋的黑客,我怎样才能以安全的方式做到这一点?
【问题讨论】:
-
你说得对,这很奇怪,但严格来说,
const成员函数不是必需线程安全的。标准库容器提供了这种保证,并且可以要求它们包含的元素。但该标准实际上并不要求所有类型都遵守此约定。例如,出于性能原因或类似原因,类型可能会在const成员函数中以非线程安全的方式执行一些内部缓存。 -
@AdrielJr 这是一个错误的假设。成员函数上的
const仅意味着您可以在const实例上调用该函数,这意味着对象的可见状态不应更改。但是内部状态可能会发生很大变化。见mutable。在这种情况下,COW 似乎不太可能发生,但可能会发生类似惰性求值的情况。 -
@AdrielJr const!=pure (我希望它出现在语言中)。 const 方法可以轻松地对指针或引用持有的成员执行非常量操作,更不用说
mutable关键字了。现在,人们可以争论这是一种好还是坏的风格或语言缺陷;底线是: const 只能指逻辑 constness。如果它也是二进制不变性,那很好,但它不能在语言级别上强制执行。 -
@AdrielJr 您可以将多种可能且自洽的定义归因于具有不同粒度和范围级别的“恒定”和“不可变”。不可能用一个关键字来描述它们。我很高兴我们有
const和const成员函数。对于大多数其他语言来说,情况并非如此。我希望该语言能让我们定义额外的限定符,例如const和volatile。 -
@ArielJr
mutable通常被认为是代码异味,除非应用于同步变量,例如互斥锁或条件变量(可以在读取时更改);缓存有时也适用于此。你可能会问 const_cast 的存在,它有它的位置,它被滥用是一个完全不同的故事。
标签: c++ qt openmp qpainterpath