2013-02-22 20 views
8

Mam wątki OpenMP, które piszą do konsoli przez cout i cerr. To oczywiście nie jest bezpieczne, ponieważ dane wyjściowe mogą być przeplatane. mógłby zrobić coś jakwiele wątków zapisujących na std :: cout lub std :: cerr

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

byłoby ładniej, gdyby mógł zastąpić cerr z wersją wątku bezpieczny, podobne do podejścia opisane w instrukcji valgrind DRD (http://valgrind.org/docs/manual/drd-manual.html#drd-manual.effective-use), która obejmuje uzyskanie klasy z std :: ostreambuf . Idealnie na końcu zastąpiłbym cerr moim własnym carem gwintowanym, np. po prostu:

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

Taka klasa może drukować na konsoli, gdy tylko napotka "końcówkę". Nie mam nic przeciwko temu, że linie z różnych wątków są przeplatane, ale każda linia powinna pochodzić tylko z jednego wątku.

Nie bardzo rozumiem, jak działa to strumieniowanie w C++, jest zbyt skomplikowane. Czy ktoś ma taką klasę lub może mi pokazać, jak stworzyć taką klasę do tego celu?

+0

proszę nie sugerować printf ..;) – Wolfgang

+0

* „To oczywiście nie jest bezpieczne” * - To nie jest prawda w C++ 11, chyba, że ​​celowe działanie, aby to prawda . –

+0

Twój tytuł mówi 'cout', a nie' cerr'. – Barmar

Odpowiedz

0

Można to zrobić dziedzicząc std::basic_streambuf i zastąpić prawidłowe funkcje, aby zapewnić bezpieczeństwo wątków. Następnie użyj tej klasy dla swoich obiektów strumieniowych.

8

Można zastosować podejście podobne do konstruktora ciągów. Tworzenie klasy non-szablon:

  • oferuje szablonie operator<< do wstawienia do tego obiektu
  • buduje wewnętrznie do std::ostringstream
  • zrzuca zawartość na zniszczenie

Nieostrożne podejście:

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

Używany jako:

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

Albo w bardziej złożonych scenariuszy:

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

Trzeba będzie dodać więcej przeciążeń jeśli chcesz manipulatorów, można użyć write lepiej niż fprintf dla zapisu atomowej w destructor lub std::cerr , możesz generalizować, aby miejsce docelowe było przekazywane do konstruktora (std::ostream/deskryptor pliku/FILE*),

+0

Myślę, że dodałbym również element "flush", który działa tak samo jak destruktor i czyści bufor wewnętrzny. Jeśli chcesz, możesz ponownie użyć tego samego atomu w kółko. Niektóre osoby mogą preferować używanie dodatkowych zakresów, jak w drugim przykładzie. –

+0

@MooingDuck: Nie jestem pewien, jaką drogę przejść ... Rozumiem, o co prosisz, ale widzę, że zakres pozwala mi zignorować zawartość, gdy patrzę na logikę, a nie na ślady (nasza struktura logowania pozwala na podobne konstrukty). Oznacza to, że gdy jest używany poprawnie (tzn. Nie miesza logiki z logowaniem), można użyć zakresu do analizy zawartości i upewnić się, że nie ma prawdziwej logiki, po czym nie muszę próbować interpretować wewnętrznych pętli robię, jeśli patrzę na logikę całej funkcji. –

20

Jak inni zwrócili uwagę, w C++ 11, std::coutjest wątkowo bezpieczny.

Jednak jeśli używasz go jak

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

z różnych wątków, wyjście może wciąż być przeplatane, ponieważ każdy << to nowy wywołanie funkcji, która może być poprzedzona dowolnym wywołaniu funkcji na innym wątku.

Aby uniknąć przeplatania bez#pragma omp critical - które zablokować wszystko - można wykonać następujące czynności:

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

Trzy rozmowy pisanie 123 do strumienia dzieją się tylko w jednym wątku lokalną, niepodlegającą -shared obiekt, dlatego nie mają wpływu na żadne inne wątki. Następnie jest tylko jedno wywołanie współdzielonego strumienia wyjściowego std::cout, w którym kolejność elementów 123 jest już ustalona, ​​a zatem nie zostanie pomieszana.

0

Nie mam wystarczającej reputacji, aby opublikować komentarz, ale chciałem opublikować mój dodatek do klasy AtomicWriter, aby obsługiwać std :: endl i zezwalać na używanie innych strumieni oprócz std :: cout. Oto ona:

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