【问题标题】:Return a QString from a function - thread safe?从函数返回 QString - 线程安全?
【发布时间】:2014-08-28 05:27:43
【问题描述】:

我是 Qt 新手——但这可能是一个非常基本的 C++ 问题。我有一个返回 QString 的简单函数:

QString testclass::myfunc(int i)
{
    QString result;
    switch (i) {
    case 1: result = "one"; break;
    case 2: result = "two"; break;
    }
    return result;
}

这样安全吗? c 编译器是否确保返回值在内存中保留足够长的时间以供调用函数使用? (或者这是否会导致内存损坏)。如果是后者,返回 QString 的正确方法是什么? (结果变量必须是静态的吗?结果必须是测试类的成员变量吗?)

QString 包含常量是否重要? (什么 id case 3 将结果分配给随机字符串)

如果 myfunc 是我想从不同线程调用的静态方法怎么办?我是否必须通过引用传递一个额外的 Qstring 以确保每个调用者都有自己的变量(并返回 void)?


这是实际的功能(稍微清理一下)- 请注意,这个功能是 STATIC 以防万一:

QString L::toString(const L::editions &level)
{
    QString levelStr;
    switch (level) {
        case L::one:
            levelStr = "one";
            break;
        case L::two:
            levelStr = "two";
            break;
        case L::three:
            levelStr = "thre";
            break;
        default:
            levelStr = "Internal Error";
            break;
    }
    return levelStr;
}

然而 valgrind 抱怨(第 121 行是 'levelStr = "one";')

34 bytes in 1 blocks are definitely lost in loss record 197 of 716
  in L::toString(L::edition const&) in /mnt/lserver2/data/development/haast/src/linfo.cpp:121
  1: malloc in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so
  2: QArrayData::allocate(unsigned long, unsigned long, unsigned long, QFlags<QArrayData::AllocationOption>) in /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  3: QString::QString(int, Qt::Initialization) in /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  4: /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  5: QString::fromUtf8_helper(char const*, int) in /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  6: QString::fromUtf8(char const*, int) in <a href="file:///opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:492" >/opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:492</a>
  7: QString::operator=(char const*) in <a href="file:///opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:609" >/opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:609</a>
  8: L::toString(L::Lconst&amp;) in <a 

【问题讨论】:

  • 这个函数返回对象(不是对象上的指针),所以它是安全的(因为有一个 QString 的复制构造函数会将局部变量result 的内容复制到调用者的外部变量中)
  • 我是这么认为的....但是看看我上面添加的 valgrind 输出
  • 我最近升级到了最新的 qt (5.3.1),突然间我在 Qt 中的 valgrind 向我展示了很多让我摸不着头脑的漏洞。如果有已知的 Qt/valgrind 问题,请告诉我。
  • 如果您收到大量 valgrind 泄漏报告,那么我首先怀疑您的 Qt Creator、Qt 开发库和/或 valgrind 过滤器在某种程度上不匹配。您可以尝试创建最短的代码来为您获取报告,然后请其他人对其进行测试。或者(重新)安装 Qt SDK 并使用它和它附带的 Qt Creator(而不是你的 Linux 发行版的 Qt Creator 和 Qt dev libs 包)。
  • 我正在使用直接从 Qt 下载的 Qt SDK (5.3.1),以及可用于我的发行版 (Fedora 20) 的最新 valgrind。有没有办法确认兼容性?

标签: c++ qt function return-value qstring


【解决方案1】:

http://qt-project.org/doc/qt-5/qstring.html#details

QString 类提供了一个 Unicode 字符串。

QString 存储一串 16 位 QChar,其中每个 QChar 对应 一个 Unicode 4.0 字符。 (上面代码值的Unicode字符 65535 使用代理对存储,即两个连续的 QChars。)

Unicode 是一种国际标准,支持大多数书写 今天使用的系统。它是 US-ASCII (ANSI X3.4-1986) 的超集 和 Latin-1 (ISO 8859-1),所有 US-ASCII/Latin-1 字符都是 在相同的代码位置可用。

在幕后,QString 使用隐式共享(写时复制)来 减少内存使用并避免不必要的数据复制。这 还有助于减少存储 16 位字符的固有开销 而不是 8 位字符。

除了QString,Qt还提供了QByteArray类来存储 原始字节和传统的 8 位以 '\0' 结尾的字符串。对于大多数 目的,QString 是您要使用的类。它贯穿始终 Qt API 和 Unicode 支持确保您的应用程序 如果你想扩展你的应用程序,将很容易翻译 在某个时候市场。 QByteArray 的两种主要情况 合适的时候需要存储原始二进制数据,以及何时 内存保护至关重要(就像在嵌入式系统中一样)。

基本上,QString 非常棒,几乎不用担心。您可以随心所欲地使用它。如果您因过于频繁地附加字符串而遇到任何类型的减速,则可以使用一种特殊的方法来使用字符串生成器,但根据我的经验,在尝试使 QString 变得更好之前,还有很多其他地方需要改进。

并直接回答您的问题:

这样安全吗? c 编译器是否确保返回值在内存中保留足够长的时间以供调用函数使用? (或者这是否会导致内存损坏)。如果是后者,返回 QString 的正确方法是什么? (结果变量必须是静态的吗?结果必须是测试类的成员变量吗?)

在上述所有情况下,它都是安全的。只要任何函数具有 QString 的句柄,共享指针等就会将其保存在内存中。一旦它完全超出范围,它就会自行清理。

QString 包含常量是否重要? (什么 id case 3 将结果分配给随机字符串)

不,没关系。

如果 myfunc 是我想从不同线程调用的静态方法怎么办?我是否必须通过引用传递一个额外的 Qstring 以确保每个调用者都有自己的变量(并返回 void)?

你应该用跨线程保护来包装它,比如QMutexLocker

更新:QMutexLocker 示例

// In your constructor

m_mutex = new QMutex();


// When accessing a shared element across threads

{
    QMutexLocker locker(m_mutex);
    // Accessing a variable is now threadsafe!
    m_sharedDataString += "!";
}

希望对您有所帮助。

【讨论】:

    【解决方案2】:

    QString 是一个值类,如果你不能从函数中返回它就没有用了。这里与std::string 没有什么不同。两者都可以按照你演示的方式使用。

    您所指的“常量字符串”的概念是虚构的。没有这样的事情。语句result = "foo"; 不会产生某种特殊的字符串。这不是 C,您没有返回 const char * - 这是有充分理由的。

    线程安全方面与字符串没有太大关系。您显示的方法主体可能是静态方法,因为它不使用任何实例的数据。它也是一个纯函数,根本无法访问任何共享状态。这样的纯函数根据定义是线程安全的,因为它们不访问共享状态。您可能希望使用更接近您的问题并实际演示一些线程问题的示例来修改您的问题。

    QString 与其他 Qt 的隐式共享值类一样,只要一个特定实例只能从一个线程访问,它就是线程安全的。实例可能是从另一个字符串实例分配或复制的,这并不重要。例如:

    QString a = "foo";
    {
      QFutureSynchronizer sync;
      sync.addFuture(QtConcurrent::run([&a] { a.append("bar"); }));
      sync.addFuture(QtConcurrent::run([&a] { a.append("baz"); }));
    }
    // This is not thread safe: a might be concurrently accessed from two
    // threads. The behavior is undefined. It might format your hard drive.
    
    QString c = "Homer";
    QString d = c;
    {
      QFutureSynchronizer sync;
      sync.addFuture(QtConcurrent::run([&c] { c.append("'s"); }));
      sync.addFuture(QtConcurrent::run([&d] { d.append(" and Elmer's"; }));
    }
    // This is thread safe. One of the threads will safely deep-copy the
    // c's contents.
    Q_ASSERT(c == "Homer's");
    Q_ASSERT(d == "Homer and Elmer's");
    

    【讨论】:

    • 很好地使用了 lambda 函数。我还在考虑尽快学习QtConcurrent。我对QThread 非常满意,我还没有离开它。感谢您的见解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多