<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Iterating shared QMap on multiple threads]]></title><description><![CDATA[<p dir="auto">Hi,</p>
<p dir="auto">I have multiple threads (3 in my test) that iterates the same QMap at the same time, I notice one of them gets stuck inside the for loop forever with the code below:</p>
<pre><code>// entries data has been set before this and never changes
// so this grabs the reference, not copying
QMap&lt;QString, QStringList&gt; const&amp; entries = GetEntries();

for (auto iter = entries.cbegin(); iter != entries.cend(); iter++)
{
    QStringList const&amp; list = iter.value();
    // do work...
}
</code></pre>
<p dir="auto">I have then checked the QMap interator example in <a href="https://doc.qt.io/qt-6/qmap-iterator.html" target="_blank" rel="noopener noreferrer nofollow ugc">https://doc.qt.io/qt-6/qmap-iterator.html</a>, which initialize "end = map.cend()" so I changed the code to:</p>
<pre><code>for (auto iter = entries.cbegin(), end = entries.cend(); iter != end; iter++)
</code></pre>
<p dir="auto">which fixed the issue and never got stuck again.</p>
<p dir="auto">What's the reason behind "iter != entries.cend()" not working?</p>
]]></description><link>https://forum.qt.io/topic/164019/iterating-shared-qmap-on-multiple-threads</link><generator>RSS for Node</generator><lastBuildDate>Fri, 11 Sep 2026 11:13:56 GMT</lastBuildDate><atom:link href="https://forum.qt.io/topic/164019.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 30 Dec 2025 23:31:54 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Thu, 08 Jan 2026 08:37:16 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/jonb">@<bdi>JonB</bdi></a> said in <a href="/post/835075">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">Without getting deep into a "nuanced" discussion, could you just summarise why these std ones are more suited here than the Qt ones, please?</p>
</blockquote>
<p dir="auto">I would say the most important point for this would be the "detaching" issue. Especially in the context of multithreading it is quite hard to figure out when detaches might happen. I guess the best way with QMap is to sprinkle <code>qAsConst</code> (or now even <code>std::as_const</code>) as often as possible. std::map just doesn't have this problem, so it is easier to get right with the STL compared to Qt.</p>
]]></description><link>https://forum.qt.io/post/835289</link><guid isPermaLink="true">https://forum.qt.io/post/835289</guid><dc:creator><![CDATA[SimonSchroeder]]></dc:creator><pubDate>Thu, 08 Jan 2026 08:37:16 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Thu, 01 Jan 2026 13:11:43 GMT]]></title><description><![CDATA[<p dir="auto">I guess it's also an option to use QMap::find() to get a const iterator which will not be a copy of the data, unless I misremember stuff.</p>
]]></description><link>https://forum.qt.io/post/835093</link><guid isPermaLink="true">https://forum.qt.io/post/835093</guid><dc:creator><![CDATA[sierdzio]]></dc:creator><pubDate>Thu, 01 Jan 2026 13:11:43 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 16:15:45 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/jonb">@<bdi>JonB</bdi></a> said in <a href="/post/835075">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">Is it because they do not get into references or temporaries or detaches or what?</p>
</blockquote>
<p dir="auto">std::map operator[] returns a reference whereas QMap operator[] const returns a temporary. That's the problem here.</p>
]]></description><link>https://forum.qt.io/post/835076</link><guid isPermaLink="true">https://forum.qt.io/post/835076</guid><dc:creator><![CDATA[Christian Ehrlicher]]></dc:creator><pubDate>Wed, 31 Dec 2025 16:15:45 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 16:08:16 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/igkh">@<bdi>IgKh</bdi></a> said in <a href="/post/835072">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">Basically yes. It is more nuanced than that, but discussing that would be just an academic exercise. As said, I think you are better served by std::vector and std::map for this use case.</p>
</blockquote>
<p dir="auto">Without getting deep into a "nuanced" discussion, could you just summarise why these <code>std</code> ones are more suited here than the Qt ones, please?  Is it because they do not get into references or temporaries or detaches or what?  And the problem/difference would  not arise/matter if multi-thread was not involved?</p>
]]></description><link>https://forum.qt.io/post/835075</link><guid isPermaLink="true">https://forum.qt.io/post/835075</guid><dc:creator><![CDATA[JonB]]></dc:creator><pubDate>Wed, 31 Dec 2025 16:08:16 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 15:54:21 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/igkh">@<bdi>IgKh</bdi></a> Thanks for the answer! I have switch to using std::map now</p>
]]></description><link>https://forum.qt.io/post/835074</link><guid isPermaLink="true">https://forum.qt.io/post/835074</guid><dc:creator><![CDATA[BrianL]]></dc:creator><pubDate>Wed, 31 Dec 2025 15:54:21 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 14:42:01 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/brianl">@<bdi>BrianL</bdi></a> said in <a href="/post/835070">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">So even if I change my code to use T&amp; operator[], it would still invalidate currently in-use iterator from other threads?</p>
</blockquote>
<p dir="auto">Basically yes. It is more nuanced than that, but discussing that would be just an academic exercise. As said, I think you are better served by <code>std::vector</code> and <code>std::map</code> for this use case.</p>
]]></description><link>https://forum.qt.io/post/835072</link><guid isPermaLink="true">https://forum.qt.io/post/835072</guid><dc:creator><![CDATA[IgKh]]></dc:creator><pubDate>Wed, 31 Dec 2025 14:42:01 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 14:33:59 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/igkh">@<bdi>IgKh</bdi></a> said in <a href="/post/835068">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">ANY non-const method on a CoW Qt container will first <code>detach()</code> the caller, even if it didn't end up modifying anything. The detach itself is thread-safe, as the copy-on-write semantics are implemented with atomic operations, but it does cause some iterator invalidation.</p>
</blockquote>
<p dir="auto">So even if I change my code to use <code>T&amp; operator[]</code>, it would still invalidate currently in-use iterator from other threads?</p>
<pre><code>Entries &amp;EntryDatabase::GetEntries(const QString &amp;path, LanguageType language)
{
    EntryDatabase&amp; database = GetDatabase();
    if (database.contains(path))
    {
        LanguageEntries&amp; languageEntries = database[path];
        if (languageEntries.contains(language))
        {
            return languageEntries[language];
        }
    }

    static OCREntries emptyEntries;
    return emptyEntries;
}
</code></pre>
]]></description><link>https://forum.qt.io/post/835070</link><guid isPermaLink="true">https://forum.qt.io/post/835070</guid><dc:creator><![CDATA[BrianL]]></dc:creator><pubDate>Wed, 31 Dec 2025 14:33:59 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 14:15:32 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/christian-ehrlicher">@<bdi>Christian-Ehrlicher</bdi></a> <a class="plugin-mentions-user plugin-mentions-a" href="/user/brianl">@<bdi>BrianL</bdi></a></p>
<p dir="auto">The <code>LanguageEntries const&amp; languageEntries = database[path];</code> part is indeed problematic, but not because of what you think. <code>database</code> is a reference to a constant, so the const version of <code>operator[]</code> is called. However do notice that unlike the non-const version it returns a <code>T</code>, not a <code>T&amp;</code>. Assigning it to a const  l-value reference performs lifetime extension of the temporary that got returned until the end of the scope, but you are playing with fire doing it like that (e.g if you tried to return it). Due to the Copy-on-Write nature of Qt containers (which will lead me to the next point), they should generally be passed and assigned by value and not by reference (which might seem strange, but they are all actually shared-ownership reference wrappers anyway).</p>
<p dir="auto">On the other hand <code>GetEntryDatabase()[path][language]</code> is exactly a mutating call, since it invokes the non-const <code>operator[]</code>. ANY non-const method on a CoW Qt container will first <code>detach()</code> the caller, even if it didn't end up modifying anything. The detach itself is thread-safe, as the copy-on-write semantics are implemented with atomic operations, but it does cause some iterator invalidation. The Qt documentation actually has a specific section on this - <a href="https://doc.qt.io/qt-6/containers.html#implicit-sharing-iterator-problem" target="_blank" rel="noopener noreferrer nofollow ugc">Implicit sharing iterator problem</a>.</p>
<p dir="auto">That specific case of detaching operation that doesn't actually modify the container is why it looks like if you fix the end iterator it works fine. It doesn't really. In such cases you should stick to external synchronization, or perhaps use the STL containers - they aren't thread safe as well, but their semantics are more obvious to C++ programmers.</p>
]]></description><link>https://forum.qt.io/post/835068</link><guid isPermaLink="true">https://forum.qt.io/post/835068</guid><dc:creator><![CDATA[IgKh]]></dc:creator><pubDate>Wed, 31 Dec 2025 14:15:32 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 14:13:11 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/christian-ehrlicher">@<bdi>Christian-Ehrlicher</bdi></a><br />
ohh I see it now, I was trying to avoid using <code>.value()</code>  because that returns a copy, but didn't notice <code>operator[] const</code> also returns a copy....</p>
<p dir="auto">In that case is there no way to return a reference from <code>QMap</code> unless I made it return a non-const reference?</p>
]]></description><link>https://forum.qt.io/post/835067</link><guid isPermaLink="true">https://forum.qt.io/post/835067</guid><dc:creator><![CDATA[BrianL]]></dc:creator><pubDate>Wed, 31 Dec 2025 14:13:11 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 14:08:03 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/brianl">@<bdi>BrianL</bdi></a> said in <a href="/post/835064">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">so path should already existed in database?</p>
</blockquote>
<p dir="auto">You're right here but</p>
<p dir="auto"><a href="https://doc.qt.io/qt-6/qmap.html#operator-5b-5d-1" target="_blank" rel="noopener noreferrer nofollow ugc">https://doc.qt.io/qt-6/qmap.html#operator-5b-5d-1</a></p>
<p dir="auto">operator[] const returns a value. So the reference you return goes out of scope.</p>
]]></description><link>https://forum.qt.io/post/835066</link><guid isPermaLink="true">https://forum.qt.io/post/835066</guid><dc:creator><![CDATA[Christian Ehrlicher]]></dc:creator><pubDate>Wed, 31 Dec 2025 14:08:03 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 14:03:16 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/christian-ehrlicher">@<bdi>Christian-Ehrlicher</bdi></a><br />
I'm sorry if I'm missing something 😅, as I said I've checked</p>
<pre><code>if (database.contains(path))
</code></pre>
<p dir="auto">before calling</p>
<pre><code>LanguageEntries const&amp; languageEntries = database[path];
</code></pre>
<p dir="auto">so path should already existed in database?</p>
]]></description><link>https://forum.qt.io/post/835064</link><guid isPermaLink="true">https://forum.qt.io/post/835064</guid><dc:creator><![CDATA[BrianL]]></dc:creator><pubDate>Wed, 31 Dec 2025 14:03:16 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 13:57:15 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/brianl">@<bdi>BrianL</bdi></a> said in <a href="/post/835062">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">anguageEntries const&amp; languageEntries = database[path];</p>
</blockquote>
<p dir="auto">And here database container gets modified when the path does not yet exists. So the container might grow.<br />
Use a proper locking mechanism</p>
]]></description><link>https://forum.qt.io/post/835063</link><guid isPermaLink="true">https://forum.qt.io/post/835063</guid><dc:creator><![CDATA[Christian Ehrlicher]]></dc:creator><pubDate>Wed, 31 Dec 2025 13:57:15 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 13:55:31 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/christian-ehrlicher">@<bdi>Christian-Ehrlicher</bdi></a> But I have made sure the path exist first before calling database[path] tho?</p>
<pre><code>if (database.contains(path))
{
    LanguageEntries const&amp; languageEntries = database[path];
</code></pre>
]]></description><link>https://forum.qt.io/post/835062</link><guid isPermaLink="true">https://forum.qt.io/post/835062</guid><dc:creator><![CDATA[BrianL]]></dc:creator><pubDate>Wed, 31 Dec 2025 13:55:31 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 13:41:46 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/brianl">@<bdi>BrianL</bdi></a> said in <a href="/post/835059">Iterating shared QMap on multiple threads</a>:</p>
<blockquote>
<p dir="auto">LanguageEntries const&amp; languageEntries = database[path];</p>
</blockquote>
<p dir="auto">Here you modify database when path is not yet in there. The same goes for <code>GetEntryDatabase()[path][language]</code>.<br />
So my your code shows what I already told you - you modify the container from different threads which results in undefined behavior.</p>
]]></description><link>https://forum.qt.io/post/835061</link><guid isPermaLink="true">https://forum.qt.io/post/835061</guid><dc:creator><![CDATA[Christian Ehrlicher]]></dc:creator><pubDate>Wed, 31 Dec 2025 13:41:46 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 11:38:37 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/christian-ehrlicher">@<bdi>Christian-Ehrlicher</bdi></a> It definitely wasn't modified after initialization, but there might be some container quirk I wasn't aware about, I'll provided the code for GetEntries() as context:</p>
<p dir="auto">Database.h</p>
<pre><code>using Entries = QMap&lt;QString, QStringList&gt;;
using LanguageEntries = QMap&lt;LanguageType, Entries&gt;;
using EntryDatabase = QMap&lt;QString, LanguageEntries&gt;;

// this class is a singleton
class Database
{
private:
    static Database&amp; instance();
    static EntryDatabase&amp; GetEntryDatabase();

    static bool EnsureDatabase(QString const&amp; path);
    static Entries const&amp; GetEntries(QString const&amp; path, LanguageType language);

    EntryDatabase m_entryDatabase;
}
</code></pre>
<p dir="auto">Database.cpp</p>
<pre><code>Database&amp; Database::instance()
{
    static Database database;
    return database;
}

EntryDatabase &amp;Database::GetEntryDatabase()
{
    return instance().m_entryDatabase;
}

bool Database::EnsureDatabase(const QString &amp;path)
{
    // check if database already exist
    EntryDatabase&amp; database = GetEntryDatabase();
    if (database.contains(path)) return true;

    // inserts into database, this is only run on GUI thread
    // BEFORE any thread get created that calls GetEntries()
    LanguageEntries languageEntries;
    for (...)
    {
        Entries entries;
        for (...)
        {
            entries.insert(it.key(), valueList);
        }
        languageEntries.insert(language, entries);
    }
    database.insert(path, languageEntries);
    return true;
}

const Entries &amp;Database::GetEntries(const QString &amp;path, LanguageType language)
{
    EntryDatabase const&amp; database = GetEntryDatabase();
    if (database.contains(path))
    {
        LanguageEntries const&amp; languageEntries = database[path];
        if (languageEntries.contains(language))
        {
            return GetEntryDatabase()[path][language];
        }
    }

    static Entries emptyEntries;
    return emptyEntries;
}
</code></pre>
]]></description><link>https://forum.qt.io/post/835059</link><guid isPermaLink="true">https://forum.qt.io/post/835059</guid><dc:creator><![CDATA[BrianL]]></dc:creator><pubDate>Wed, 31 Dec 2025 11:38:37 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 10:38:54 GMT]]></title><description><![CDATA[<p dir="auto">Read-only is safe as long as noone else is modifying it or accessing the code with non-const functions which the op is doing for sure somehwere - otherwise the problem would not arise.</p>
]]></description><link>https://forum.qt.io/post/835058</link><guid isPermaLink="true">https://forum.qt.io/post/835058</guid><dc:creator><![CDATA[Christian Ehrlicher]]></dc:creator><pubDate>Wed, 31 Dec 2025 10:38:54 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 10:18:00 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/christian-ehrlicher">@<bdi>Christian-Ehrlicher</bdi></a><br />
I read the OP's question earlier.  So that I understand: if they genuinely do <em>not</em> "modify them somewhere", and the OP's original shown code is all there is and does reading only, would it then be OK if that reading code is called from multiple threads, potentially simultaneously?</p>
]]></description><link>https://forum.qt.io/post/835056</link><guid isPermaLink="true">https://forum.qt.io/post/835056</guid><dc:creator><![CDATA[JonB]]></dc:creator><pubDate>Wed, 31 Dec 2025 10:18:00 GMT</pubDate></item><item><title><![CDATA[Reply to Iterating shared QMap on multiple threads on Wed, 31 Dec 2025 09:27:57 GMT]]></title><description><![CDATA[<p dir="auto">Don't access containers from two different threads without proper locking. I would guess you modify them somewhere.</p>
]]></description><link>https://forum.qt.io/post/835047</link><guid isPermaLink="true">https://forum.qt.io/post/835047</guid><dc:creator><![CDATA[Christian Ehrlicher]]></dc:creator><pubDate>Wed, 31 Dec 2025 09:27:57 GMT</pubDate></item></channel></rss>