<?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[Deadlock]]></title><description><![CDATA[<p>Folgendes Szenario:</p>
<pre><code>#include &lt;thread&gt;
#include &lt;mutex&gt;
#include &lt;list&gt;

class service;

class user {
	std::recursive_mutex mutex_;
	std::shared_ptr&lt;service&gt; service_ptr_; 
public:
	user(std::shared_ptr&lt;service&gt; s) : mutex_(), service_ptr_(s) { }

	void operation() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(mutex_);
		// some operations
		service_ptr_-&gt;inform_all();
	}

	void inform() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(mutex_);
		// inform the client about something
	}
};

class service {
	std::recursive_mutex list_mutex_;
	std::list&lt;std::shared_ptr&lt;user&gt;&gt; users_;
public:
	service() : list_mutex_(), users_() { }

	void add_user() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(list_mutex_);
		users_.push_back(std::shared_ptr&lt;user&gt;(new user(this)));
	}

	void inform_all() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(list_mutex_);
		for(const auto&amp; user : users_)
			user-&gt;inform();
	}

};

...

void some_multithreaded_fun(std::shared_ptr&lt;service&gt; service_ptr) {
	service_ptr-&gt;inform_all(); // zack
	...
}

void some_other_mt_fun(std::shared_ptr&lt;user&gt; user_ptr) {
	user_ptr-&gt;operation(); // zack
}
</code></pre>
<p>Die Funktionen <code>some_multithreaded_fun</code> und <code>some_other_mt_fun</code> laufen i.A. gleichzeitig in unterschiedlichen Threads. Wie man sehen kann, wird die erste Funktion erst den Mutex für die Liste und dann jedes User-Mutex einzeln locken, während die andere Funktion genau das umgekehrte tut, also passieren Deadlocks.<br />
Was kann ich tun? Ich habe schon versucht, etwas mit <code>std::lock</code> zu spielen, aber damit habe ich mir immer nur die Kapselung der Klassen zerstört und ende darin, dass alle friends sind, was ich nicht will.</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/319807/deadlock</link><generator>RSS for Node</generator><lastBuildDate>Fri, 24 Jul 2026 20:59:10 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/319807.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 04 Sep 2013 08:24:11 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 08:25:07 GMT]]></title><description><![CDATA[<p>Folgendes Szenario:</p>
<pre><code>#include &lt;thread&gt;
#include &lt;mutex&gt;
#include &lt;list&gt;

class service;

class user {
	std::recursive_mutex mutex_;
	std::shared_ptr&lt;service&gt; service_ptr_; 
public:
	user(std::shared_ptr&lt;service&gt; s) : mutex_(), service_ptr_(s) { }

	void operation() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(mutex_);
		// some operations
		service_ptr_-&gt;inform_all();
	}

	void inform() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(mutex_);
		// inform the client about something
	}
};

class service {
	std::recursive_mutex list_mutex_;
	std::list&lt;std::shared_ptr&lt;user&gt;&gt; users_;
public:
	service() : list_mutex_(), users_() { }

	void add_user() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(list_mutex_);
		users_.push_back(std::shared_ptr&lt;user&gt;(new user(this)));
	}

	void inform_all() {
		std::unique_lock&lt;std::recursive_mutex&gt; lck(list_mutex_);
		for(const auto&amp; user : users_)
			user-&gt;inform();
	}

};

...

void some_multithreaded_fun(std::shared_ptr&lt;service&gt; service_ptr) {
	service_ptr-&gt;inform_all(); // zack
	...
}

void some_other_mt_fun(std::shared_ptr&lt;user&gt; user_ptr) {
	user_ptr-&gt;operation(); // zack
}
</code></pre>
<p>Die Funktionen <code>some_multithreaded_fun</code> und <code>some_other_mt_fun</code> laufen i.A. gleichzeitig in unterschiedlichen Threads. Wie man sehen kann, wird die erste Funktion erst den Mutex für die Liste und dann jedes User-Mutex einzeln locken, während die andere Funktion genau das umgekehrte tut, also passieren Deadlocks.<br />
Was kann ich tun? Ich habe schon versucht, etwas mit <code>std::lock</code> zu spielen, aber damit habe ich mir immer nur die Kapselung der Klassen zerstört und ende darin, dass alle friends sind, was ich nicht will.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2350163</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350163</guid><dc:creator><![CDATA[Jodocus]]></dc:creator><pubDate>Wed, 04 Sep 2013 08:25:07 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 10:02:04 GMT]]></title><description><![CDATA[<p>Wie waere es, wenn du operation() so umschreibst, dass sie den Mutex nur so lange wie noetig haelt?</p>
<pre><code>void operation() {
        {
            std::unique_lock&lt;std::recursive_mutex&gt; lck(mutex_);
            // some operations
        }
        service_ptr_-&gt;inform_all();
    }
</code></pre>
<p>Abgesehen von deinem Problem hast du hier in deinem Code noch ein paar kleinere Unschoenheiten.<br />
- Der User braucht keinen shared_ptr auf den Service, da der Service offenbar immer laenger lebt als der User. Nimm stattdessen eine Referenz.<br />
- Wenn der Service den User besitzt, kannst du hierfuer evtl unique_ptr einsetzen.<br />
- Nimm in push_back make_shared anstatt explizit mit einem genewten User zu konstruieren. Das erlaubt es, den Refcount im selbst Speicherblock wie den User zu allozieren.<br />
- Gibt es einen Grund, warum du eine std::list verwendest? (Hast du evtl. schon mal ueber concurrent-access Container nachgedacht, die mittels atomics implementiert sind?)<br />
- some_multithreaded_fun und some_other_mt_fun sollten den shared_ptr per const-ref nehmen, um sinnloses inkrementieren und dekrementieren des Refcounts zu vermeiden. Das gilt generell fuer shared_ptr als Parameter, auch fuer den Konstruktor des Users z.B.<br />
- Du musst die mutex Konstruktoren nicht explizit aufrufen. Das passiert von selbst bei Typen mit Default-Konstruktor.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2350189</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350189</guid><dc:creator><![CDATA[Kellerautomat]]></dc:creator><pubDate>Wed, 04 Sep 2013 10:02:04 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 11:28:39 GMT]]></title><description><![CDATA[<blockquote>
<p>&gt; Wie waere es, wenn du operation() so umschreibst, dass sie den Mutex nur so lange wie noetig haelt?</p>
</blockquote>
<p>Sieht irgendwie fehleranfällig aus. Wenn nun zwischen den Operationen und dem <code>inform_all()</code> ein anderer Thread am Zustand des Users etwas ändert, dann werden alle anderen Clients ja mit dem veralteten Zustand informiert. Der Zustand des Users sollte besser so lange vor anderen Threads geschützt sein, bis er an alle anderen User verteilt wurde, denke ich, sonst könnten andere User wiederrum auf die falsche Information falsch reagieren etc.</p>
<p>Ich habe <a href="http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163?pgno=1" rel="nofollow">hier</a> einen sehr interessanten Artikel über hierachische Locks gefunden. Der sagt aus, dass man, wenn man z.B. ein Mutex auf Leven N gelockt hat, nur noch Mutexe locken kann, die unterhalb dieses Levels liegen. Diese Hierachie soll aus dem Software-Design hervorkommen, mein Design ruiniert das allerdings, wie man schön an <code>some_other_mt_fun</code> sehen kann:<br />
Erst wird der User-Mutex gelockt, dann der User-Listen-Mutex und dann wieder der User-Mutex. Ich muss also irgendwas an dem Design ändern.</p>
<blockquote>
<p>&gt; - Der User braucht keinen shared_ptr auf den Service, da der Service offenbar immer laenger lebt als der User. Nimm stattdessen eine Referenz.</p>
</blockquote>
<p>War ja nur exemplarisch.</p>
<blockquote>
<p>&gt; - Wenn der Service den User besitzt, kannst du hierfuer evtl unique_ptr einsetzen.</p>
</blockquote>
<p>Andere Strukturen halten den User evtl. auch, also ist shared_ptr die bessere Wahl, besonders, falls sich das noch ändern sollte.</p>
<blockquote>
<p>&gt; - Nimm in push_back make_shared anstatt explizit mit einem genewten User zu konstruieren. Das erlaubt es, den Refcount im selbst Speicherblock wie den User zu allozieren.</p>
</blockquote>
<p>Okay.</p>
<blockquote>
<p>&gt; - Gibt es einen Grund, warum du eine std::list verwendest? (Hast du evtl. schon mal ueber concurrent-access Container nachgedacht, die mittels atomics implementiert sind?)</p>
</blockquote>
<p>Hat die Stdlib denn sowas?</p>
<blockquote>
<p>&gt; - some_multithreaded_fun und some_other_mt_fun sollten den shared_ptr per const-ref nehmen, um sinnloses inkrementieren und dekrementieren des Refcounts zu vermeiden. Das gilt generell fuer shared_ptr als Parameter, auch fuer den Konstruktor des Users z.B.</p>
</blockquote>
<p>Stimmt.</p>
<blockquote>
<p>&gt; - Du musst die mutex Konstruktoren nicht explizit aufrufen. Das passiert von selbst bei Typen mit Default-Konstruktor.</p>
</blockquote>
<p>Aber nach Standard muss doch die Liste in der Deklarationsreihenfolge sein. Kann ich Member mit Default-Konstruktor da einfach überspringen?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2350221</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350221</guid><dc:creator><![CDATA[Jodocus]]></dc:creator><pubDate>Wed, 04 Sep 2013 11:28:39 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 11:57:38 GMT]]></title><description><![CDATA[<p>Jodocus schrieb:</p>
<blockquote>
<p>Der Zustand des Users sollte besser so lange vor anderen Threads geschützt sein, bis er an alle anderen User verteilt wurde, denke ich, sonst könnten andere User wiederrum auf die falsche Information falsch reagieren etc.</p>
</blockquote>
<p>Ich sehe nicht so wirklich, weshalb das nötig sein sollte. Zur Not machst du halt eine Kopie mit dem neuen Status und verteilst diese.</p>
<p>Aber angenommen, du bist wirklich sicher, dass das der einzige gangbare Weg ist, dann ist der recursive_mutex im service immer noch unnötig.</p>
<pre><code class="language-cpp">class service {
    std::mutex mutex;
    std::deque&lt;user&gt; users;
    std::deque&lt;user&gt;::iterator begin;
    std::atomic&lt;std::deque&lt;user&gt;::iterator&gt; end;
public:
    service() : begin(users.begin()), end(users.end()) { }

    void add_user() {
        std::unique_lock&lt;std::mutex&gt; guard(mutex); // könnte man mit geeigneter Datenstruktur auch lock-free machen
        users.emplace_back(this);
        end.store(users.end());
    }

    void inform_all() {
        std::deque&lt;user&gt;::iterator end = this-&gt;end.load();
        for (auto it=begin; it!=end; ++it)
           it-&gt;inform();
    }

};
</code></pre>
<p>Desweiteren würde ich user vollständig von service entkoppeln und eine std::function&lt;void()&gt; bzw. Templates verwenden um inform auszuführen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2350229</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350229</guid><dc:creator><![CDATA[fgbawerg]]></dc:creator><pubDate>Wed, 04 Sep 2013 11:57:38 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 14:25:41 GMT]]></title><description><![CDATA[<p>Das inform_all ist aber nicht threadsafe bezüglich Löschen aus der Liste. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f61e.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--disappointed_face"
      title=":("
      alt="😞"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2350283</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350283</guid><dc:creator><![CDATA[Jodocus]]></dc:creator><pubDate>Wed, 04 Sep 2013 14:25:41 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 14:51:34 GMT]]></title><description><![CDATA[<p>Dann würde ich mir dringend überlegen das Design zu überarbeiten. Muss das wirklich so gemacht werden? Im Moment sieht das nämlich langsamer aus, als wenn alles Single-threaded wäre bzw. nur operation() multithreaded.</p>
<p>Ansonsten ein Quick-Fix von deinem broken Design:</p>
<pre><code class="language-cpp">class user {
    std::mutex operation_mutex_;
    std::mutex access_mutex_;
    std::shared_ptr&lt;service&gt; service_ptr_;
public:
    user(std::shared_ptr&lt;service&gt; s) : service_ptr_(s) { }

    void operation() {
        // Angepasste Lösung von Kellerautomat:
        // Nur ein Thread darf operieren
        std::unique_lock&lt;std::mutex&gt; lck(operation_mutex_);
        {
          std::unique_lock&lt;std::mutex&gt; lck(access_mutex_);
          // some operations
        }
        // access wird freigegeben, jetzt kann die Liste informieren
        service_ptr_-&gt;inform_all();
    }

    void inform() {
        std::unique_lock&lt;std::mutex&gt; lck(access_mutex_);
        // inform the client about something
    }
};
</code></pre>
<p>operation: user-op { 1. user-acc, 2. liste }<br />
inform_all: liste { user-acc }</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2350290</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350290</guid><dc:creator><![CDATA[fgbawerg]]></dc:creator><pubDate>Wed, 04 Sep 2013 14:51:34 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 14:57:25 GMT]]></title><description><![CDATA[<p>Im Prinzip kannst du dir natürlich aussuchen in welcher Reihenfolge du deine Mutexen locken willst - welche Funktion es &quot;falsch&quot; macht ist dann je nachdem unterschiedlich.</p>
<p>Die Übliche Variante wäre aber zu definieren:<br />
Mutexen werden in Reihenfolge der Funktionsaufrufe gelockt. Also High-Level vor Low-Level.<br />
Das setzt natürlich voraus dass man eine halbwegs saubere Dependency-Struktur hat, was aber meist nicht das grosse Problem ist.<br />
Weiters darf man *keine* Mutexen gelockt halten während man Callbacks macht -- also beliebigen Code aufruft den man von anderen Programmteilen untergejubelt bekommt.</p>
<p>Nach dieser Auffassung hast du hier zwei Problemfunktionen:<br />
* service::inform_all hält ne Mutex während eines Callbacks<br />
* user::operation hält ne Mutex während es eine Funktion aufruft die wiederrum Callbacks aufruft</p>
<p>Der &quot;Fix&quot; für service::inform_all ist...<br />
* Lock Mutex<br />
* Kopiere Userliste<br />
* Unlock Mutex<br />
* Gehe Kopie der Userliste durch und rufe Callbacks auf</p>
<p>Dummerweise kann es dabei aber zu Problemen kommen wenn z.B. ein User gelöscht wird -- er könnte noch in einer lokalen Kopie der Userliste enthalten sein, und dann würden wir eine Funktion auf ein gelöschtes Objekt aufrufen.<br />
Also brauchen wir shared_ptr.<br />
Die Kopie der Userliste verwendet dann z.B. weak_ptr, und während der eigentliche Callback läuft hält ein temoprärer shared_ptr das Objekt am Leben.</p>
<p>Dass ein User schon gar nicht mehr in der User Liste enthalten ist während er einen Callback bekommt kann trotzdem sein, aber zumindest können wir dadurch garantieren dass das User Objekt noch existiert.</p>
<p>Und die Lösung für user::operation ist trivial: einfach den Lock freigeben bevor die inform_all Funktion aufgerufen wird.</p>
<p>Jodocus schrieb:</p>
<blockquote>
<p>Ich habe <a href="http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163?pgno=1" rel="nofollow">hier</a> einen sehr interessanten Artikel über hierachische Locks gefunden. Der sagt aus, dass man, wenn man z.B. ein Mutex auf Leven N gelockt hat, nur noch Mutexe locken kann, die unterhalb dieses Levels liegen. Diese Hierachie soll aus dem Software-Design hervorkommen,</p>
</blockquote>
<p>Das ist im Prinzip das was ich geschrieben habe, nur besser erklärt <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":-)"
      alt="🙂"
    /></p>
<blockquote>
<p>mein Design ruiniert das allerdings, wie man schön an <code>some_other_mt_fun</code> sehen kann:<br />
Erst wird der User-Mutex gelockt, dann der User-Listen-Mutex und dann wieder der User-Mutex. Ich muss also irgendwas an dem Design ändern.</p>
</blockquote>
<p>Japp.<br />
Wobei es hier zwei Möglichkeiten gibt:</p>
<ol>
<li>
<p>Du definierst dass service &quot;über&quot; user steht. Dann ist die Implementierung deiner inform_all Methode noch OK, das Problem ist dann alleine der Aufruf von inform_all aus der user Klasse, während du die user Mutex gelockt hast.</p>
</li>
<li>
<p>Du siehst das it-&gt;inform() als Callback an, d.h. sagst &quot;da darf beliebiger Code drinnen stehen&quot;. Das ist die Variante die ich oben angenommen habe. In dem Fall musst du schon während des it-&gt;inform() Aufrufs sicherstellen dass keine Mutexen gelockt sind.</p>
</li>
</ol>
]]></description><link>https://www.c-plusplus.net/forum/post/2350291</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350291</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Wed, 04 Sep 2013 14:57:25 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock on Wed, 04 Sep 2013 15:51:12 GMT]]></title><description><![CDATA[<p>Jodocus schrieb:</p>
<blockquote>
<blockquote>
<p>&gt; - Gibt es einen Grund, warum du eine std::list verwendest? (Hast du evtl. schon mal ueber concurrent-access Container nachgedacht, die mittels atomics implementiert sind?)</p>
</blockquote>
<p>Hat die Stdlib denn sowas?</p>
</blockquote>
<p>Leider (noch) nicht. Aber solange kannst du dir mit <a href="https://www.threadingbuildingblocks.org/" rel="nofollow">TBB</a> behelfen. Es gibt da unter anderem concurrent_vector, concurrent_unordered_set, concurrent_unordered_map und concurrent_hash_map. Je nachdem, was fuer deinen Fall am ehesten passt.</p>
<p>Jodocus schrieb:</p>
<blockquote>
<blockquote>
<p>&gt; - Du musst die mutex Konstruktoren nicht explizit aufrufen. Das passiert von selbst bei Typen mit Default-Konstruktor.</p>
</blockquote>
<p>Aber nach Standard muss doch die Liste in der Deklarationsreihenfolge sein. Kann ich Member mit Default-Konstruktor da einfach überspringen?</p>
</blockquote>
<p>Ja.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2350311</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2350311</guid><dc:creator><![CDATA[Kellerautomat]]></dc:creator><pubDate>Wed, 04 Sep 2013 15:51:12 GMT</pubDate></item></channel></rss>