std::thread di gestione: utilizzo e la migliore pratica

Sto cercando di capire thread dopo l’utilizzo di loro un po ‘in Java e sto un po’ sconcertati.
Due domande:

  • Posso estendere la mia classe da un thread o devo gestire il filo all’interno della classe, tramite un gestore?
  • Come faccio a salvare disse filo conduttore? std::thread in sé e per sé non sembra il nome di un tipo.

Qualsiasi prod nella giusta direzione sarebbe molto apprezzato.

Come posso interpretare questo messaggio?

src/CHandler.h:27:9: error: 'thread' in namespace 'std' does not name a type
         std::thread _thread;
         ^

E qui è il mio tentativo di estendere thread:

src/CHandler.h:17:30: error: expected class-name before '{' token
 class CHandler : std::thread {
                              ^

Completo, fastidioso intestazione:

#ifndef __projectm__CHandler__
#define __projectm__CHandler__

#include <set>
#include <vector>
#include <thread>

#include "CListener.h"

class CHandler {
    public:
        virtual bool subscribe(std::shared_ptr<CListener> aListener);
        virtual bool unsubscribe(std::shared_ptr<CListener> aListener);

        virtual bool hasSubscriber(std::shared_ptr<CListener> aListener);

        virtual ~CHandler() {}

    protected:
        std::thread _thread;
        std::vector<std::weak_ptr<CListener> > _subscribers;
        std::set<const CListener *> _subscribersSet;

        virtual void run();
};

#endif /* defined(__projectm__CDefaultHandler__) */

Versione del compilatore:

bash-3.1$ g++ --version
g++.exe (GCC) 4.8.1

Il makefile (un casino, so – ancora imparare questa cosa sanguinosa):

CC=g++

OUTFILE=game

BINDIR=bin
SRCDIR=src
OBJDIR=obj

CFLAGS=
LDFLAGS=-std=c++0x



all: core

# Ядро проекта.
core: $(OBJDIR)/main.o $(OBJDIR)/CGame.o $(OBJDIR)/CHandler.o $(OBJDIR)/CListener.o
    $(CC) $(CFLAGS) $(wildcard $(OBJDIR)/*.o) -o $(BINDIR)/$(OUTFILE)

$(OBJDIR)/main.o: $(OBJDIR)
    $(CC) $(LDFLAGS) $(SRCDIR)/main.cpp -c -o $(OBJDIR)/main.o

$(OBJDIR)/CGame.o: $(OBJDIR)
    $(CC) $(LDFLAGS) $(SRCDIR)/CGame.cpp -c -o $(OBJDIR)/CGame.o

$(OBJDIR)/CHandler.o: $(OBJDIR)
    $(CC) $(LDFLAGS) $(SRCDIR)/CHandler.cpp -c -o $(OBJDIR)/CHandler.o

$(OBJDIR)/CListener.o: $(OBJDIR)
    $(CC) $(LDFLAGS) $(SRCDIR)/CListener.cpp -c -o $(OBJDIR)/CListener.o

# Создаем директорию для объектов, если ее нет.
$(OBJDIR):
    mkdir $(OBJDIR)

main.o: $(SRC)/main.cpp
std::thread è una classe. Non capisco il tuo problema.
scusa, intendevo dire “non il nome di un tipo”. Se si tenta di memorizzare un oggetto thread in una variabile non ha funzionato.
Si tratta di un tipo. Una classe è un tipo.
Ho capito, ma il compilatore ancora butta e non un tipo di errore.
Questo non è il problema, ma i nomi che contengono due consecutivi caratteri di sottolineatura (__projectm__CHandler__) e i nomi che iniziano con un carattere di sottolineatura seguito da una lettera maiuscola sono riservati per l’attuazione. Non li uso.

OriginaleL’autore Maxim Kumpan | 2013-10-17

5 risposte

  1. 4

    Bjarne Stroustrup mostra alcuni esempi di utilizzo di std::thread nel suo C++11 FAQ. L’esempio più semplice simile a questo:

    #include<thread>
    
    void f();
    
    struct F {
        void operator()();
    };
    
    int main()
    {
        std::thread t1{f};  //f() executes in separate thread
        std::thread t2{F()};    //F()() executes in separate thread
    }

    In generale, std::thread non è destinato a essere ereditata. Il passaggio di una funzione per eseguire in modo asincrono nel costruttore.

    Se il tuo compilatore non supporta std::thread, si potrebbe utilizzare Boost.Thread invece. È abbastanza compatibile, in modo che si può sostituire da std::thread una volta che il compilatore supporta.

    Questo sembra molto bello, ma dichiarando std::thread _thread; restituisce error: 'thread' in namespace 'std' does not name a type
    Questo probabilmente significa che si dispone di alcune includono avvitato. È impossibile diagnosticare l’errore senza il codice sorgente completo. Hai provato a compilare l’esempio che ho postato? Se funziona, allora si conosce il suo non è il compilatore. Probabilmente si dovrebbe fare qualche lettura su come include il lavoro in C++, in quanto sono abbastanza diversi dal pacchetto importazioni in Java.
    Buona domanda. Cercherò di compilazione che esempio e vedere se il thread opere in sé e per sé.
    No, non funziona. g++ -std=c++0x -o test test.cpp \\ test.cpp: In function 'int main()': \\ test.cpp:11:5: error: 'thread' is not a member of 'std'
    Niente è più permanente di una soluzione temporanea. Ma non credo che la Spinta sarà più un problema come eredità filettatura soluzione. Farò una prova. Grazie per l’aiuto.

    OriginaleL’autore Björn Pollex

  2. 8

    La raccomandazione è non di ereditare da std::thread: non ha virtual metodo comunque. Vorrei anche raccomandare di non utilizzare la composizione.

    Il problema principale con std::thread è che un thread come presto come è costruito (tranne se è possibile utilizzare il costruttore di default). Pertanto, un certo numero di situazioni sono difficili:

    //BAD: Inheritance
    class Derived: std::thread {
    public:
        Derived(): std::thread(&Derived::go, this), _message("Hello, World!") {}
    
        void go() const { std::cout << _message << std::endl; }
    
    private:
        std::string _message;
    };

    Il thread può eseguire go prima _message è costruito, portando ad una data razza.

    //BAD: First Attribute
    class FirstAttribute {
    public:
        FirstAttribute(): _thread(&Derived::go, this), _message("Hello, World!") {}
    
        void go() const { std::cout << _message << std::endl; }
    
    private:
        std::thread _thread;
        std::string _message;
    };

    Stesso problema, il thread può eseguire go prima _message è costruito, portando ad una data razza.

    //BAD: Composition
    class Safer {
    public:
        virtual void go() const = 0;
    
    protected:
        Safer(): _thread(&Derived::go, this) {}
    
    private:
        std::thread _thread;
    };
    
    class Derived: Safer {
        virtual void go() const { std::cout << "Hello, World!\n"; }
    };

    Stesso problema, il thread può eseguire go prima Derived è costruito, portando ad una data razza.


    Come si può vedere, se si eredita o di composizione, è molto facile involontariamente causare una data razza. Utilizzando std::thread come ultimo attributo di una classe di lavoro… se si può garantire nessuno, deriva da questa classe.

    Sembra quindi meglio per me, per ora, consiglia di utilizzare solo std::thread come variabili locali. Si noti che se si utilizza il async impianto, non anche la gestione di una std::thread da soli.

    OriginaleL’autore Matthieu M.

  3. 7

    Uno dei problemi con l’utilizzo di std::thread come un disadorno variabile locale è che non è eccezione cassetta di sicurezza. Devo ammettere che spesso mi sento in colpa di questo a me stesso quando dimostrando piccolo HelloWorlds.

    Tuttavia è bene sapere esattamente cosa si sta entrando, quindi, ecco una spiegazione più dettagliata di eccezione per gli aspetti di sicurezza utilizzando std::thread:

    #include <iostream>
    #include <thread>
    
    void f() {}
    void g() {throw 1;}
    
    int
    main()
    {
        try
        {
            std::thread t1{f};
            g();
            t1.join();
        }
        catch (...)
        {
            std::cout << "unexpected exception caught\n";
        }
    }

    Nell’esempio di cui sopra, ho un “grande”, “occasionalmente” genera un’eccezione. In genere si desidera catturare e gestire le eccezioni prima che bolla fino a main. Tuttavia, come ultima risorsa, main stesso è avvolto in un try-catch-all. In questo esempio ho stampate semplicemente che qualcosa di grave è accaduto e chiudere. In un esempio più realistico si potrebbe dare il vostro cliente una possibilità di salvare il lavoro, o per liberare memoria e spazio su disco, avviare un processo diverso che i file di un bug report, etc.

    Sembra buono, giusto? Purtroppo sbagliato. Quando si esegue questa operazione, il risultato è:

    libc++abi.dylib: terminating
    Abort trap: 6

    Non ho dato il mio cliente la notifica che qualcosa è andato storto, prima di tornare da main normalmente. Mi aspettavo questa uscita:

    unexpected exception caught

    Invece std::terminate() ho chiamato.

    Perché?

    Come si scopre, ~thread() assomiglia a questo:

    thread::~thread()
    {
        if (joinable())
            terminate();
    }

    Così, quando g() getta, t1.~thread() viene eseguito durante la rimozione dello stack, e senza t1.join() sempre chiamato. Così t1.~thread() chiamate std::terminate().

    Non mi chiedete il perchè. E ‘ una lunga storia, e mi manca l’obiettività di dire unbiasedly.

    Indipendentemente da ciò, è necessario conoscere questo comportamento, e la guardia contro di essa.

    Una possibile soluzione è tornare al wrapper di design, magari utilizzando l’ereditarietà privata come primo, proposto dall’OP e messo in guardia contro le altre risposte:

    class CHandler
        : private std::thread
    {
    public:
        using std::thread::thread;
        CHandler() = default;
        CHandler(CHandler&&) = default;
        CHandler& operator=(CHandler&&) = default;
        ~CHandler()
        {
            if (joinable())
                join();  //or detach() if you prefer
        }
        CHandler(std::thread t) : std::thread(std::move(t)) {}
    
        using std::thread::join;
        using std::thread::detach;
        using std::thread::joinable;
        using std::thread::get_id;
        using std::thread::hardware_concurrency;
    
        void swap(CHandler& x) {std::thread::swap(x);}
    };
    
    inline void swap(CHandler& x, CHandler& y) {x.swap(y);}

    L’intento è quello di creare un nuovo tipo, dire CHandler che si comporta come un std::thread, tranne che per il suo distruttore. ~CHandler() dovrebbe chiamare join() o detach() nel suo distruttore. Ho scelto join() sopra. Ora si può semplicemente sostituire CHandler per std::thread nel mio codice di esempio:

    int
    main()
    {
        try
        {
            CHandler t1{f};
            g();
            t1.join();
        }
        catch (...)
        {
            std::cout << "unexpected exception caught\n";
        }
    }

    e l’uscita è ora:

    unexpected exception caught

    come previsto.

    Perché scegliere join() invece di detach() in ~CHandler()?

    Se si utilizza join(), quindi la rimozione dello stack del thread principale si blocca fino a quando f() completa. Questo può essere ciò che si vuole, o non può essere. Non posso rispondere a questa domanda per voi. Solo tu puoi decidere questo problema di progettazione per la vostra applicazione. Considerare:

    //simulate a long running thread
    void f() {std::this_thread::sleep_for(std::chrono::minutes(10));}

    Il main() thread ancora lanciare un’eccezione sotto g(), ma ora si blocca durante lo svolgimento, e a soli 10 minuti più tardi stampa:

    unexpected exception caught

    e di uscita. Forse a causa di riferimenti o risorse che vengono utilizzate all’interno di f(), questo è ciò che è necessario avere accadere. Ma se non lo è, allora è possibile invece:

        ~CHandler()
        {
            if (joinable())
                detach();
        }

    e quindi il programma immediatamente l’uscita di “imprevisto” rilevata eccezione di andata e ritorno, anche se f() è ancora occupato sgranocchiare via (dopo main() restituisce f() sarà annullato con forza come parte di un normale arresto dell’applicazione).

    Forse avete bisogno di join()-on-unwinding per alcuni dei vostri thread e detach()-on-unwinding per gli altri. Forse questo ti porta a due CHandler-come wrapper, o a una politica basata su wrapper. Il comitato è stato in grado di formare un consenso per una soluzione, e quindi è necessario decidere ciò che è giusto per voi, o che vivono con terminate().

    Questo fa un uso diretto di std::thread molto, molto basso a livello di comportamento. Ok per Hello World, ma in un’applicazione reale, migliori incapsulato via in un mid-level gestore, tramite l’ereditarietà privata o come un membro dati privato. La buona notizia è che in C++11 che metà del gestore a livello di ora può essere scritto in modo portatile (in cima std::thread) invece di scrivere per il sistema operativo o un 3rd-party lib come è necessario in C++98/03.

    OriginaleL’autore Howard Hinnant

  4. 0

    Prima di tutto cosa compilatore e la versione del compilatore stai usando?
    std::thread è abbastanza nuovo e non è stato implementato in alcuni di loro fino a poco tempo fa. Che potrebbe essere il tuo problema.

    In secondo luogo hai

    #include <thread> 

    In terzo luogo (e questo non è il vostro problema immediato) che non è come usare il thread in c++. Non si eredita, si crea un’istanza di passaggio nella funzione che si desidera eseguire.

    std::thread mythread = std::thread(my_func);

    (È possibile passare un sacco di più di una semplice funzione se)

    Io sto usando il g++ da mingw pacchetto tramite custom makefile. LDFLAGS=-std=c++0x. Includere thread non ha aiutato. Semplicemente la dichiarazione di std::thread mythread ancora genera un errore.
    che versione di g++?
    aggiunto g++ versione di domanda.
    Questo è il problema. mingw non supporta std::thread su windows, in questo momento, per quanto ne so. Certamente non è recente. L’attuale versione di visual c++ su windows.
    Non rispondo alla domanda ma alcuni commenti su questo qui programmers.stackexchange.com/questions/195639/…

    OriginaleL’autore jcoder

  5. 0

    Assicurarsi che quando si compila e il collegamento, utilizzare:

    g++ -std=c++11 your_file.cpp -o your_program 

    a spippolare con LDFLAGS sarà solo aiutare il collega, non si compila.

    No ice, l’aggiunta di -std=c++11 per CFLAGS non ha aiutato.
    Per il g++ (v4.9.2) in linux, ho dovuto aggiungere due bandiere di supporto <filo> std::thread programmi: -std=c++11 -pthread

    OriginaleL’autore doron

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *