2015-06-10 24 views
7

Sto creando un software in tempo reale in cui ho un ciclo infinito principale su main() e thread utilizzati per leggere ed elaborare i dati.C++ std :: vettore di indipendente std :: threads

Uno dei problemi è mantenere uno std::vector di thread in esecuzione per inviare segnali a loro e per monitorare l'esecuzione. Così ho messo insieme questo codice:

#include <iostream> 
#include <string> 
#include <vector> 
#include <thread> 
#include <chrono> 

namespace readerThread { 

    void start(int id) 
    { 
     while (1) 
     { 
      std::cout << "Reader " << id << " running..." << std::endl; 
      std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
     } 
    } 

} 


int main() 
{ 

     int readers[] = { 1, 2, 3 }; 

     std::vector<std::thread> readerThreads; 

     for (int &reader : readers) 
     { 
      std::thread th(readerThread::start, reader); 
      readerThreads.push_back(th); 
     } 

     while(true) 
     { 
      std::cout << "Waiting..." << std::endl; 
      std::this_thread::sleep_for(std::chrono::milliseconds(10000)); 
     } 

     return 0; 
} 

E 'Indifferente anche compilare, questo errore:

In file included from /usr/local/include/c++/5.1.0/x86_64-unknown-linux-gnu/bits/c++allocator.h:33:0, 
       from /usr/local/include/c++/5.1.0/bits/allocator.h:46, 
       from /usr/local/include/c++/5.1.0/string:41, 
       from /usr/local/include/c++/5.1.0/bits/locale_classes.h:40, 
       from /usr/local/include/c++/5.1.0/bits/ios_base.h:41, 
       from /usr/local/include/c++/5.1.0/ios:42, 
       from /usr/local/include/c++/5.1.0/ostream:38, 
       from /usr/local/include/c++/5.1.0/iostream:39, 
       from main.cpp:1: 
/usr/local/include/c++/5.1.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::thread; _Args = {const std::thread&}; _Tp = std::thread]': 
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:256:4: required from 'static std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > = void]' 
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:402:16: required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]' 
/usr/local/include/c++/5.1.0/bits/stl_vector.h:917:30: required from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::thread; _Alloc = std::allocator<std::thread>; std::vector<_Tp, _Alloc>::value_type = std::thread]' 
main.cpp:37:30: required from here 
/usr/local/include/c++/5.1.0/ext/new_allocator.h:120:4: error: use of deleted function 'std::thread::thread(const std::thread&)' 
    { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } 
    ^
In file included from main.cpp:4:0: 
/usr/local/include/c++/5.1.0/thread:126:5: note: declared here 
    thread(const thread&) = delete; 
    ^

I fili areindependent, quindi non ho bisogno di chiamare join sul programma principale, né su qualsiasi thread ...

Così, qui sono i miei dubbi:

Perché il mio codice non viene compilato?

È il modo corretto di memorizzare il vettore di thread?

Grazie per l'aiuto ...

PS: Original code here:

+0

Se si intende arrestare correttamente l'applicazione, è necessario chiamare 'join()' prima che l'oggetto thread possa distruggere. Oppure chiama 'detach()' per staccare il thread dall'oggetto. Altrimenti riceverai la chiamata 'terminate()' nel thread destructor. – gomons

+0

gomons, non ho capito. join() manterrà l'esecuzione del thread principale, e non è desiderato qui ... – Mendes

+0

Puoi anche usare 'readerThreads.emplace_back (readerThread :: start, reader);', cosa significa gomons è che devi() 'o' detach() 'un'istanza di thread prima dell'esecuzione del suo distruttore, altrimenti viene chiamato' terminate() '. Ma i tuoi thread sembrano abbastanza felici di funzionare per sempre, quindi questo non è un problema nell'esempio sopra. – Praetorian

risposta

11

È necessario usare qualcosa come

readerThreads.push_back(move(th)); 

Questo renderà th un rvalue, e causare la ctor mossa da chiamare . Il copy ctor di thread era disabled di progettazione (vedere Anthony Williams C++ Concurrency In Action).

+2

Sì! Ora ricordo che da altre letture ... Grazie per l'aiuto ... Lavoro come un fascino ... – Mendes

6
/usr/local/include/c++/5.1.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::thread; _Args = {const std::thread&}; _Tp = std::thread]': 
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:256:4: required from 'static std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > = void]' 
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:402:16: required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]' 
/usr/local/include/c++/5.1.0/bits/stl_vector.h:917:30: required from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::thread; _Alloc = std::allocator<std::thread>; std::vector<_Tp, _Alloc>::value_type = std::thread]' 
main.cpp:37:30: required from here 
/usr/local/include/c++/5.1.0/ext/new_allocator.h:120:4: error: use of deleted function 'std::thread::thread(const std::thread&)' 
    { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } 

Consente di staccare leggermente questo dorso.

error: use of deleted function 'std::thread::thread(const std::thread&)' 

Il codice sta facendo qualcosa che tenta di introdurre un std::thread.

required from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) 

push_back è il colpevole.

std::thread non è copiabile: cosa sarebbe media per copiare un thread?

std::thread t1([](){}); 
std::thread t2 = t1; 

Così istanze di std::thread oggetti sono destinati ad essere proprietari unici. A parte la semplice confusione, ne deriverebbe molto dolore.

Sono tuttavia mobili.

std::thread t1([](){}); 
std::thread t2 = std::move(t1); 

t1 è non più un descrittore filo valida, il filo si stava descrivendo è ora di proprietà t2.

Per posizionare tali oggetti in un contenitore è possibile utilizzare std::move o std::emplace/std::emplace_back.

std::vector<std::thread> threads; 
threads.push_back(std::move(std::thread([](){}))); 
threads.emplace_back([](){}); 

Mentre il codice si sta concentrando su questo particolare problema, mi permetta di sottolineare che lo standard C++ dichiara come un errore per un distruttore filo da richiamare mentre un thread è ancora collegato e non si è unito.

int main() { 
    std::thread t1([](){ while (true) { std::this_thread::yield(); } }; 
} 

Quando termina principali, t1. ~ Filo() viene chiamato, che rileva che il filo è ancora collegato e non è unito, ciò solleva un'eccezione causando un crash arresto.

È necessario join() il thread, in attesa che si interrompa in esecuzione o detach() esso. Avrai bisogno di un modo per dire al thread di interrompere se si desidera utilizzare join() e se si detach() il programma potrebbe uscire nel mezzo del thread facendo qualcosa come scrivere dati ecc, è possibile introdurre un bug grave.

#include <thread> 
#include <chrono> 
#include <future> 

int main() { 
    std::promise<void> cnx_promise; 
    std::shared_future<void> cnx_future; 

    std::thread t1([cnx_future]() { 
     while (cnx_future.valid()) { 
     std::this_thread::yield(); 
     } 
    }); 

    std::this_thread::sleep_for(std::chrono::seconds(1)); 

    cnx_promise.set_value(); 

    t1.join(); 
} 

Qui usiamo la promessa di lasciare che il filo sapere quando è il momento di smettere di correre, ma è possibile utilizzare le variabili di condizione, segnali, ecc, o anche solo un semplice std::atomic<bool> ok_to_run { true }; che si prova per falso.

1

Un'altra variante che funziona è creare l'oggetto thread nella chiamata vector.push_back. Non è necessario chiamare std :: move in questo caso perché è già un valore rval (quindi verrà spostato).

for (int &reader : readers) 
    readerThreads.push_back(std::thread(readerThread::start, reader)); 
+0

La risposta più semplice e migliore. – JulianSoto