Geben Sie eine Ressource in einem anderen Thread sicher frei

9

Ich habe eine Klasse ähnlich wie:

%Vor%

Instanzen von A werden auf dem GUI-Thread zerstört, wo sie den letzten Verweis auf ein Foo halten können. Der Destruktor für Foo läuft möglicherweise sehr lange, was zu einer unerwünschten Pause in meinem GUI-Thread führt. Ich möchte, dass Foo in diesem Fall auf einem separaten Thread zerstört wird, Foo's sind in sich abgeschlossen und es ist nicht kritisch Sie werden sofort freigelassen.

Momentan verwende ich ein Muster wie dieses:

%Vor%

Nehmen Sie es im Wesentlichen in einem Lambda auf und sperren Sie es, bis es vom Objekt losgelassen wird. Gibt es einen besseren Weg, dies zu erreichen? Dies scheint nur komplizierter als es sein muss.

    
sellsword 29.09.2015, 21:58
quelle

2 Antworten

0

A sollte nicht für die Zerstörung des Ziels von m_pFoo verantwortlich sein. Die Zerstörung der Ressource, auf die ein shared_ptr zeigt, liegt in der Verantwortung von shared_ptr . Daher sollte es meiner Meinung nach nicht möglich sein, den Thread zu verwalten, auf dem die Zerstörung des realen Objekts in ~A stattfindet.

Anstatt einen neuen Typ von intelligenten Zeigern zu implementieren, der Ihren Bedürfnissen entspricht, denke ich, dass ein guter Kompromiss darin besteht, die Logik des Löschens des zugrundeliegenden Objekts aus ~A herauszunehmen und sie zu einem benutzerdefinierten Deleter zu verschieben stellen Sie das shared_ptr beim Bau bereit. Wenn Sie mit Ihrer derzeitigen Deallokationsstrategie zufrieden sind, halte ich dies für einen zufriedenstellenden Ansatz. Aber ich stimme auch den anderen zu, dass Sie möglicherweise Strategien untersuchen möchten, die nicht das Erstellen eines neuen Threads für jede Freigabe enthalten.

Sie finden hier die Dokumente zur Deletion des smart_ptr . Scrollen Sie nach unten zu "Konstruktoren, die einen Deleter verwenden" (Sie können auch die Dokumentation zu der spezifischen Boost-Version nachschlagen, die Sie verwenden).

    
Brian Fairservice 30.09.2015 13:10
quelle
0

Wie Mark Ransom bereits in den Kommentaren vorgeschlagen hat, könntest du einen dedizierten Destruktions-Thread verwenden, der das to übernimmt - Zerstörte Objekte aus einer Arbeitswarteschlange und lassen sie dann einfach auf den Boden fallen. Dies funktioniert unter der Annahme, dass, wenn Sie sich von einem Objekt entfernen, die Zerstörung des wegbewegten Objekts sehr billig ist.

Ich schlage hier eine Klasse destruction_service vor, die auf den Objekttyp templatisiert ist, den sie zerstören soll. Dies kann jede Art von Objekt sein, nicht nur geteilte Zeiger. In der Tat sind geteilte Zeiger sogar die schwierigsten, da Sie vorsichtig sein müssen, dass Sie nur std::shared_ptr zur Vernichtung einreichen, wenn der Referenzzähler einen Wert erreicht hat. Andernfalls wird das Zerstören von std::shared_ptr auf dem Zerstörungsthread grundsätzlich ein No-Op sein, außer dem Verringern der Referenzzählung. In diesem Fall wird jedoch nichts wirklich Schlechtes passieren. Sie werden nur das Objekt auf einem Thread zerstören, der es nicht tun sollte und deshalb länger als ideal blockiert werden könnte. Zum Debuggen könnten Sie assert in Ihrem Destruktor verwenden, dass Sie nicht im Hauptthread sind.

Ich benötige jedoch, dass der Typ einen Konstruktor hat, der nicht wirft und den Bau-Operator bewegt.

A destruction_service<T> behält std::vector<T> der Objekte bei, die zerstört werden sollen. Senden eines Objekts zur Zerstörung push_back() s es auf diesem Vektor. Der Worker-Thread wartet darauf, dass die Warteschlange nicht leer wird, und dann swap() s mit einer eigenen leeren std::vector<T> . Nachdem der kritische Abschnitt verlassen wurde, wird der Vektor clear() s, wodurch alle Objekte zerstört werden. Der Vektor selbst wird beibehalten, so dass er beim nächsten Mal swap() ped zurückkommen kann, was die Notwendigkeit für dynamische Speicherzuweisungen verringert. Wenn Sie sich Sorgen machen, dass die std::vector s niemals kleiner wird, sollten Sie stattdessen einen std::deque verwenden. Ich würde eine std::list nicht verwenden, weil sie Speicher für jedes Element zuweist und es ist etwas paradox, Speicher zuzuordnen, um ein Objekt zu zerstören. Der übliche Vorteil der Verwendung von std::list als Arbeitswarteschlange ist, dass Sie keinen Speicher im kritischen Bereich zuweisen müssen, aber das Zerstören der Objekte ist wahrscheinlich eine Aufgabe mit niedriger Priorität, und es ist mir egal, ob der Arbeitsthread blockiert ist etwas länger als benötigt, solange der Hauptfaden reaktionsfähig bleibt. Es gibt keine Standardmethode, um die Priorität eines Threads in C ++ festzulegen, aber wenn Sie möchten, können Sie versuchen, dem Arbeitsthread über die std::thread (im Konstruktor von native_handle ) eine niedrige Priorität zu geben. vorausgesetzt, Ihre Plattform erlaubt dies.

Der Destruktor von destruction_service wird destruction_service der Worker-Thread. Wie geschrieben, ist die Klasse nicht kopierbar und nicht beweglich. Setzen Sie es in einen intelligenten Zeiger, wenn Sie es bewegen müssen.

%Vor%

Hier ist ein einfacher Anwendungsfall:

%Vor%

Vielen Dank an Barry für diesen Code überprüfen und einige gute Vorschläge zur Verbesserung machen. Bitte beachten Sie meine Frage zu Code Review abgespeckte Version des Codes, aber ohne seine Vorschläge enthalten.

    
5gon12eder 08.10.2015 00:22
quelle

Tags und Links