2013-02-22 8 views
8

Ich habe OpenMP-Threads, die über cout und cerr auf die Konsole schreiben. Dies ist natürlich nicht sicher, da die Ausgabe verschachtelt werden kann. Ich könnte so etwas wiemehrere Threads, die auf std :: cout oder std :: cerr schreiben

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

Es wäre schöner machen, wenn cerr mit einem Thread-sichere Version ersetzen könnte, ähnlich dem Ansatz in der valgrind DRD Handbuch erklärt (http://valgrind.org/docs/manual/drd-manual.html#drd-manual.effective-use), die von std Ableiten einer Klasse beinhaltet :: ostreambuf . Idealerweise würde ich am Ende cerr durch meinen eigenen cerr ersetzen, z. simply:

Eine solche Klasse könnte auf die Konsole drucken, sobald sie auf ein "endl" stößt. Es macht mir nichts aus, wenn Zeilen aus verschiedenen Threads verschachtelt sind, aber jede Zeile sollte nur aus einem Thread kommen.

Ich verstehe nicht wirklich, wie all dieses Streaming in C++ funktioniert, es ist zu kompliziert. Hat jemand solch eine Klasse oder kann mir zeigen, wie man eine solche Klasse für diesen Zweck erstellt?

+0

bitte nicht vorschlagen printf ..;) – Wolfgang

+0

* „Das ist natürlich nicht sicher“ * - Das in C++ nicht wahr ist 11, es sei denn, Sie absichtlich Maßnahmen ergreifen, um es wahr . –

+0

Ihr Titel sagt 'cout' nicht' cerr'. – Barmar

Antwort

0

Sie könnten es tun, indem Sie erben std::basic_streambuf, und überschreiben Sie die richtigen Funktionen, um es threadsafe. Verwenden Sie diese Klasse dann für Ihre Stream-Objekte.

8

Sie können einen Ansatz verwenden, der einem Zeichenfolgengenerator ähnelt. Erstellen Sie eine Nicht-Template-Klasse, die:

  • Templat-operator<< zum Einsetzen bietet in diesem Objekt
  • intern in eine std::ostringstream
  • Dumps den Inhalt auf Zerstörung

Raue Ansatz baut:

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

Verwendung als:

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

oder in komplexeren Szenarien:

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

Sie müssen mehr Überlastungen hinzufügen, wenn Sie Manipulatoren möchten, können Sie write besser als fprintf für die Atomschreib im Destruktor verwenden oder std::cerr können Sie verallgemeinern, so dass das Ziel an den Konstruktor übergeben wird (std::ostream/Dateideskriptor/FILE*),

+0

Ich denke, ich würde auch ein 'flush'-Mitglied hinzufügen, das dasselbe wie der Destruktor tut und den internen Puffer löscht. Dann können Sie das gleiche Atom wieder und wieder verwenden, wenn Sie möchten. Manche Leute ziehen es vor, zusätzliche Bereiche zu verwenden, wie in Ihrem zweiten Beispiel. –

+0

@MooingDuck: Ich bin mir nicht sicher, welchen Weg ich gehen soll ... Ich verstehe, was Sie verlangen, aber ich finde, dass der Umfang es erlaubt, den Inhalt zu ignorieren, wenn ich die Logik betrachte und nicht die Spuren (unser Protokollierungsrahmen erlaubt Ähnliches) konstruiert). Das heißt, bei richtiger Verwendung (dh keine Logik mit Logging mischen) kann der Scope verwendet werden, um den Inhalt zu analysieren und sicherzustellen, dass keine echte Logik vorhanden ist, nach der ich nicht versuchen muss zu interpretieren, was die internen Schleifen sind tun, wenn ich auf die Logik der ganzen Funktion schaue. –

20

Wie andere hingewiesen, in C++ 11, std::coutist Thread-sicher.

Allerdings, wenn Sie es wie

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

mit verschiedenen Threads verwenden, können immer noch die Ausgabe verschachtelt werden, da jeder << ist eine neue Funktionsaufruf, der auf einem anderen Thread von jedem Funktionsaufruf vorangestellt werden kann.

Um die Verschachtelung ohne zu vermeiden ein #pragma omp critical - das alles sperren würde - Sie können folgendes tun:

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

Die drei Anrufe 123 in den Stream zu schreiben in nur einem Thread zu einem lokalen geschieht, nicht Shared-Objekt, sind daher nicht von anderen Threads betroffen. Dann gibt es nur einen Aufruf an den freigegebenen Ausgabestrom std::cout, wo die Reihenfolge der Artikel 123 bereits festgelegt ist, daher wird nicht durcheinander geraten.

0

Ich habe nicht genug Reputation, um einen Kommentar zu posten, aber ich wollte meinen Zusatz zur AtomicWriter-Klasse veröffentlichen, um std :: endl zu unterstützen und andere Streams neben std :: cout zu verwenden. Hier ist sie:

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