c++ QSettings可以被多个线程安全地访问吗?

mbskvtky  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(238)

我正在用Qt Framework写一个多线程项目,最近开始使用动态分析工具helgrindthread sanitizer来检查线程安全。
在整个应用程序中,我一直在编写一个名为QSettings的Qt类型。这个类的名义功能是在文件或注册表中存储应用程序设置,如窗口大小,公司名称和其他类型的设置(控制器增益,定时器超时,...)。
从我们阅读的文档中:

同时从多个线程或进程调用设置

QSettings是可重入的。这意味着您可以在不同的线程中同时使用不同的QSettings对象。即使QSettings对象引用磁盘上的相同文件,也可以保证这一点(或系统注册表中的相同条目)。如果通过一个QSettings对象修改设置,该更改将立即在同一位置上操作并存在于同一进程中的任何其他QSettings对象中可见。
同样从文档中我们知道只有一个函数是 * 线程安全的 *。
最后,我想记住以下部分:

void QSettings::sync()

将任何未保存的更改写入永久存储,并重新加载在此期间被另一个应用程序更改的任何设置。此函数会定期从QSettings的析构函数和事件循环中自动调用,因此您通常不需要自己调用它。
并且:

void QSettings::setAtomicSyncRequired(bool enable)

配置是否需要QSettings来执行原子保存和重新加载如果enable参数为true,(默认),sync()将只执行原子同步操作。如果这是不可能的,sync()将失败,status()将是一个错误条件。将此属性设置为false将允许QSettings直接写入配置文件并忽略任何错误试图锁定它以防止其他进程同时尝试写入。由于可能发生损坏,因此应小心使用此选项,但在某些情况下需要使用此选项,例如存在于不可写目录中的QSettings::DataFormat配置文件或DataFormat Alternate Data Streams。
所以原则上QSettings是可重入的,不能在多线程应用程序中安全使用,但如果我们使用setAtomicSyncRrequired为True,就可以做到。

我从文档中了解到的。

QSettings可以从不同的线程示例化,并以隔离的方式使用,因此每个线程都有设置的私有副本。每次调用sync()时,所有线程都以安全的方式获得相同的状态设置,前提是setAtomicSyncRequired设置为True。如果setAtomicSyncRequired设置为true,那么在文件上的写入是原子地完成的。所以这是一种好的写入方式,不是吗?如果我们使用 set(value),那么我们修改了Qsettings的本地副本,但由于sync()写入公共文件,线程将以相同的设置状态结束。
1.问题:这是正确的解释吗?
当我开始使用 helgrindthread sanitizer 时,它们会给我带来
data race
问题,因为不同的线程写同一个文件。同时访问的文件是存储
QSettings
设置的配置的文件。
1.**问题:**这是假阳性吗?
1.**问题:**QSettings的使用是否有问题?如何改进,以便在应用程序中存储所有线程的通用设置?
1.Question:为了通过sync()让所有线程设置每隔一段时间同步一次,是否应该让同一个QSettings对象只有一个指针,并设置为truesetAtomicSyncRequired

tquggr8v

tquggr8v1#

我知道sync是在多个进程之间,而不是多个线程之间。在Linux上,设置存储在一个文件中,所以你需要原子地写入它。QSaveFile为你做到了这一点。在Windows上,写入注册表项本身就已经是线程安全的,但是写入大量的项可能需要进一步的锁定。
Looking at the source code在我看来,Qt总是试图以原子方式写入。只是根据文件系统的不同,如果锁文件不起作用,这可能会失败。如果setAtomicSyncRequired为false,则写入设置将继续而不具有原子性,否则写入设置将失败。请参阅链接文件中的第1373行:

void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
{
…
    /*
        Use a lockfile in order to protect us against other QSettings instances
        trying to write the same settings at the same time.

        We only need to lock if we are actually writing as only concurrent writes are a problem.
        Concurrent read and write are not a problem because the writing operation is atomic.
    */
    QLockFile lockFile(lockFileName);
    if (!readOnly && !lockFile.lock() && atomicSyncOnly) {
        setStatus(QSettings::AccessError);
        return;
    }
…
}

字符串
QSettings本身由每个ini文件的互斥锁和一个全局互斥锁保护。我认为假设所有操作都是线程安全的是安全的。这符合我对Qt的期望。sync只需要将数据发布到其他进程,而不是同一进程中的其他线程。
当我开始使用helgrind和thread sanitizer时,它们会给我带来数据竞争问题,因为不同的线程写同一个文件。被同步访问的文件是存储QSettings设置的配置的文件。
问:这是假阳性吗?
是的,我假设这是误报。杀毒程序可能不理解锁定文件和配置文件之间的关系。
问:我使用Qsettings时有什么问题吗?如何改进它以获得一种方法来存储应用程序中所有线程的公共设置?
我不认为有什么问题。但是,要注意原子性。加载和保存单个设置是原子的,但访问一组设置不是。例如,如果一个线程更改了两个设置,而另一个线程读取这两个设置,则阅读线程可能会获得一个旧值和一个新值。
问:如果只有一个指向同一个QSetting对象的指针,并设置为true setAtomicSyncRequired,以便通过sync()每隔一小段时间同步所有线程设置,这会是一个好主意吗?
不,我不认为这是一个好主意,如果有的话,它更糟糕。像QSettings::beginGroupendGroupbeginArrayendArray这样的操作将无法正常工作,除非你添加一些外部锁定。
这种设计是为了让QSettings对象本身是线程或父对象的本地对象,并且可能是短暂的。
至于同步,请注意isAtomicSyncRequired默认为true,sync()QSettings析构函数自动调用。

相关问题