2013-02-22 14 views
8

Ho thread OpenMP che scrivono sulla console tramite cout e cerr. Questo ovviamente non è sicuro, poiché l'output può essere interlacciato. Ho potuto fare qualcosa di similethread multipli che scrivono su std :: cout o std :: cerr

#pragma omp critical(cerr) 
{ 
    cerr << "my variable: " << variable << endl; 
} 

Sarebbe bello se potesse sostituire cerr con una versione thread-safe, simile all'approccio spiegato nel manuale valgrind DRD (http://valgrind.org/docs/manual/drd-manual.html#drd-manual.effective-use) che coinvolge derivare una classe da std :: ostreambuf . Idealmente alla fine sostituirò semplicemente cerr con il mio cerr thread, ad es. semplicemente:

tcerr << "my variable: " << variable << endl; 

Tale classe potrebbe stampare sulla console non appena incontra un "endl". Non mi importa se le linee di thread diversi sono intercalate, ma ogni riga dovrebbe provenire solo da un thread.

Non ho veramente capito come tutto questo streaming in C++ funzioni, è troppo complicato. Qualcuno ha una classe o può mostrarmi come creare una classe del genere per quello scopo?

+0

favore, non suggeriscono printf ..;) – Wolfgang

+0

* "Questo, naturalmente, non è sicuro" * - Questo non è vero in C++ 11, a meno che non si prende azione intenzionale per renderlo vero . –

+0

Il tuo titolo dice 'cout' not' cerr'. – Barmar

risposta

0

Si potrebbe fare ereditando std::basic_streambuf e sovrascrivere le funzioni corrette per renderlo a prova di thread. Quindi usa questa classe per i tuoi oggetti stream.

8

È possibile utilizzare un approccio simile a un generatore di stringhe. Creare una classe non-template che:

  • offre su modelli operator<< per l'inserimento in questo oggetto
  • costruisce internamente in un std::ostringstream
  • discariche i contenuti sulla distruzione

approccio ruvido:

class AtomicWriter { 
    std::ostringstream st; 
public: 
    template <typename T> 
    AtomicWriter& operator<<(T const& t) { 
     st << t; 
     return *this; 
    } 
    ~AtomicWriter() { 
     std::string s = st.str(); 
     std::cerr << s; 
     //fprintf(stderr,"%s", s.c_str()); 
     // write(2,s.c_str(),s.size()); 
    } 
}; 

Uso come:

AtomicWriter() << "my variable: " << variable << "\n"; 

o in più scenari complessi:

{ 
    AtomicWriter w; 
    w << "my variables:"; 
    for (auto & v : vars) { 
     w << ' ' << v; 
    } 
} // now it dumps 

Sarà necessario aggiungere ulteriori sovraccarichi se si vuole manipolatori, è possibile utilizzare write meglio fprintf per la scrittura atomica nel distruttore, o std::cerr , è possibile generalizzare in modo che la destinazione sia passata al costruttore (std::ostream/descrittore di file/FILE*),

+0

Penso che aggiungerei anche un membro 'flush' che fa lo stesso del distruttore e cancella il buffer interno. Quindi puoi riutilizzare sempre lo stesso atomico se lo desideri. Alcune persone potrebbero preferire l'utilizzo di ambiti aggiuntivi come nel tuo secondo esempio. –

+0

@MooingDuck: Non so quale direzione andare ... Capisco quello che chiedi, ma trovo che l'ambito mi consente di ignorare i contenuti quando guardo la logica e non le tracce (il nostro framework di registrazione consente di costrutti). Cioè, se usato correttamente (cioè non mescolare la logica con la registrazione), lo scope può essere usato per analizzare i contenuti e assicurare che non ci sia una vera logica, dopo di che non ho bisogno di cercare di interpretare cosa i loop interni stanno facendo se guardo la logica dell'intera funzione. –

20

Come altri hanno sottolineato, in C++ 11, std::coutè thread-safe.

Tuttavia se lo si utilizza come

std::cout << 1 << 2 << 3; 

con fili diversi, l'uscita può essere ancora interleaved, poiché ogni << è una nuova chiamata di funzione che può essere preceduta da qualsiasi chiamata di funzione su un altro thread.

Per evitare interleaving senza un #pragma omp critical - che bloccare tutto - è possibile effettuare le seguenti operazioni:

std::stringstream stream; // #include <sstream> for this 
stream << 1 << 2 << 3; 
std::cout << stream.str(); 

I tre chiamate di scrittura 123 al flusso stanno accadendo in un solo thread per un locale, non -shared oggetto, quindi non sono interessati da altri thread. Quindi, c'è una sola chiamata al flusso di output condiviso std::cout, in cui l'ordine degli articoli 123 è già stato risolto, pertanto non verrà incasinato.

0

Non ho abbastanza reputazione per pubblicare un commento, ma volevo pubblicare la mia aggiunta alla classe AtomicWriter per supportare std :: endl e consentire l'utilizzo di altri flussi oltre a std :: cout. Eccolo:

class AtomicWriter { 
    std::ostringstream st; 
    std::ostream &stream; 
public: 
    AtomicWriter(std::ostream &s=std::cout):stream(s) { } 
    template <typename T> 
    AtomicWriter& operator<<(T const& t) { 
     st << t; 
     return *this; 
    } 
    AtomicWriter& operator<<(std::ostream&(*f)(std::ostream&)) { 
     st << f; 
     return *this; 
    } 
    ~AtomicWriter() { stream << st.str(); } 
};