2015-09-30 17 views
7

Gemäß dem C++ 14-Standard werden nicht statische Elementvariablen in der Reihenfolge initialisiert, in der sie in einer Klasse deklariert sind. Der unten stehende Code zur Reduzierung hängt von dieser Regel ab, um eine Thread-Funktion zu steuern. In Abhängigkeit von der Reihenfolge der Initialisierung

class foo 
{ 
    foo(): 
      keep_going{true}, 
      my_thread(&foo::go,this) 
    {} 

     void go() 
     { 
      while(keep_going) 
      check a std::condition_variable and do some work; 
     } 
     bool keep_going; 
     std::thread my_thread; 
} 

Beachten Sie, dass keep_going vor dem Thread-Objekt deklariert wird, und sollte der Faden tritt in die Go-Funktion true durch die Zeit eingestellt werden. Das ist in Ordnung und scheint in Ordnung zu sein.

Dies ist jedoch multithreaded Code und es zahlt sich paranoid zu sein, so habe ich zwei Fragen:

1 Ist es sicher, wie dies in der Größenordnung von Initialisierung verlassen? Mein reales Objekt macht keinen Sinn ohne den Verarbeitungsthread, also möchte ich es im Konstruktor setzen.

2 Ist es unsicher, anderen Code zu geben, wenn es auf relativ obskuren Dingen wie der Reihenfolge der Initialisierung beruht?

+0

Reihenfolge der Initialisierung ist nicht obskur, es ist gut definiert und es gibt nichts falsch mit diesem Code. – Salgar

+0

1) ja Reihenfolge der Initialisierung ist Teil der Spezifikationen, 2) ja - klare Dokumentation könnte helfen – BeyelerStudios

+0

Ich würde mich darauf verlassen, nur mit einem guten Kommentar zu erklären. Folgen Sie dem Zitat "Immer Code wie Ihr Code Mantainer ist ein Serienmörder, der weiß, wo Sie leben" –

Antwort

5
  1. Ist sicher nach dem Standard.

  2. Extrem unsicher. Wenige Leute sind sich dessen bewusst, und jemand, der Ihre Header-Datei verwaltet, könnte die Mitglieder mit verheerenden Folgen neu ordnen.

Ich würde mich nicht darauf verlassen.

+0

Ich gebe es mit einigen Block-Cap-Warnungen in der Header-Datei. –

2

Ich möchte den Code neu schreiben, um den Code sogar für einen Affen offensichtlich zu machen.

Ein weiteres mögliches Problem kann hier auftreten, wenn die Klasse foo die Basisklasse ist. Der Thread wird auf einem nicht vollständig konstruierten Objekt beginnen. Was passiert, wenn der Konstruktor der abgeleiteten Klasse fehlschlägt? In diesem Fall ist es besser, die Thread-Ausführung aus dem Konstruktor in die start() Methode zu verschieben.

4

Obwohl durch den Standard ist es sicher, würde ich nicht damit gehen.

Anekdote: Ich schrieb einen benutzerdefinierten ThreadPool auf Windows-Betriebssystem mit Visual Studio 2013. Ich erklärte den Thread-Pool global. Natürlich durch den Standard, werden globale Objekte zerstört, nachdem Main zurückgekehrt ist. der Thread-Pool-Destruktor pro Thread versucht, join, aber leider! eine Sackgasse. (Sie können über dieses Problem hier lesen: std::thread::join() hangs if called after main() exits when using VS2012 RC). Der Standard besagt sehr deutlich, dass es kein Problem gibt, wenn ein Thread verbunden werden kann, aber wie Sie sehen können, wurde dies nicht perfekt implementiert.

Warum erzähle ich Ihnen dieses unabhängige Problem? Weil sogar Compiler und Plattformen einige Bugs haben. Subtile Dinge werden möglicherweise nicht zu 100% korrekt in den ersten relevanten Compiler-Versionen implementiert.

Deshalb würde ich nicht mit dieser Idee gehen. Als eine Umgehung, würde ich den Thread in std::unique_ptr verpackt deklarieren und es im Konstruktor Körper initialisieren. Auf diese Weise besteht keine Möglichkeit, dass es vor keep_going initialisiert wird.

foo(): 
    keep_going{true} 
    { my_thread = std::make_unique<std::thread>(&foo::go,this); } 
Verwandte Themen