2015-09-30 14 views
7

In base allo standard C++ 14, le variabili membro non statiche vengono inizializzate nell'ordine in cui sono dichiarate in una classe. Il codice ridotto qui sotto si basa su questa regola per controllare una funzione thread.Fare affidamento sull'ordine di inizializzazione

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; 
} 

noti che keep_going viene dichiarata prima che l'oggetto filo e deve essere impostato true dal momento in cui il filo entra nella funzione go. Questo va bene e sembra funzionare bene.

Tuttavia, questo è il codice multithreaded e vale la pena di essere paranoico così ho due domande:

1 E 'sicuro di fare affidamento su ordine di inizializzazione in questo modo? Il mio vero oggetto non ha senso senza il thread di elaborazione, quindi voglio impostarlo nel costruttore.

2 È pericoloso dare il codice agli altri quando si basa su cose relativamente oscure come l'ordine di inizializzazione?

+0

L'ordine di inizializzazione non è oscuro, è ben definito e non c'è nulla di sbagliato in questo codice. – Salgar

+0

1) sì l'ordine di inizializzazione fa parte delle specifiche, 2) sì - la documentazione chiara potrebbe aiutare – BeyelerStudios

+0

Mi baserei su di esso solo con un buon commento che spiega. Segui la citazione "Codice sempre come il tuo mantainer del codice è un serial killer che sa dove vivi" –

risposta

5
  1. È sicuro secondo lo standard.

  2. Estremamente pericoloso. Poche persone ne sono consapevoli e qualcuno che gestisce il file di intestazione potrebbe riordinare i membri con conseguenze disastrose.

Non ci farei affidamento.

+0

L'ho inserito con alcuni avvisi di blocco nel file di intestazione. –

2

Mi piacerebbe riscrivere il codice per rendere il codice evidente anche a una scimmia.

Un altro potenziale problema può verificarsi quando la classe foo è la classe base. Il thread inizierà su oggetti non completamente costruiti. Cosa succederà se il costruttore della classe derivata fallisce? In questo caso è meglio spostare l'esecuzione del thread dal costruttore al metodo start().

4

Sebbene dallo standard è sicuro, non vorrei andare con esso.

Anecdote: Ho scritto un ThreadPool personalizzato su sistema operativo Windows utilizzando Visual Studio 2013. Ho dichiarato il pool di thread globale. Ovviamente secondo lo standard, gli oggetti globali vengono distrutti dopo che è stata restituita la rete principale. il distruttore del pool di thread ha provato a join ogni thread, ma ahimè! un dead-lock. (puoi leggere su questo problema qui: std::thread::join() hangs if called after main() exits when using VS2012 RC). Lo standard afferma molto chiaramente che se un thread è univoco, non c'è alcun problema ad unirlo, ma come puoi vedere, questo non è stato perfettamente implementato.

Perché sto dicendo questo problema non correlato? Perché anche i compilatori e le piattaforme hanno alcuni bug. Le cose sottili potrebbero non essere implementate correttamente al 100% nelle prime versioni del compilatore rilevanti.

Questo è il motivo per cui non andrei con questa idea. Come soluzione, dichiarare il thread racchiuso in std::unique_ptr e inizializzarlo nel corpo del costruttore. In questo modo non è possibile inizializzarlo prima del keep_going.

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