2013-02-22 5 views
8

У меня есть потоки OpenMP, которые пишут на консоль через cout и cerr. Это, конечно, небезопасно, так как вывод может чередоваться. Я мог бы сделать что-то вроденесколько потоков, записывающих в std :: cout или std :: cerr

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

Было бы лучше, если бы можно было заменить КВЖД с поточно-версией, подобный подход объясняется в руководстве Valgrind ДПДА (http://valgrind.org/docs/manual/drd-manual.html#drd-manual.effective-use), который включает в выводе класса из станда :: ostreambuf , В идеале, в конце концов, я бы просто заменил cerr своим собственным поточным cerr, например. просто:

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

Такой класс может печатать на консоль, как только он встречает «endl». Я не против, если строки из разных потоков чередуются, но каждая строка должна поступать только из одного потока.

Я не совсем понимаю, как все это работает на C++, это слишком сложно. Кто-нибудь такой класс или может показать мне, как создать такой класс для этой цели?

+0

пожалуйста не предлагают Printf ..;) – Wolfgang

+0

* «Это, конечно, не безопасно» * - Это не верно в C++ 11, если не принимать намеренного действия, чтобы сделать его истинным , –

+0

Ваше название говорит 'cout' не' cerr'. – Barmar

ответ

0

Вы можете сделать это, наследуя std::basic_streambuf и переопределить правильные функции, чтобы сделать его потоковым. Затем используйте этот класс для ваших объектов потока.

8

Вы можете использовать подход, похожий на построитель строк. Создайте класс, не шаблонный, что:

  • предлагает шаблонный operator<< для вставки в этот объект
  • внутренне встраивает в std::ostringstream
  • выгружает содержимое на уничтожение

Грубый подход:

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

Использовать как:

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

Или в более сложные сценарии:

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

Вам нужно будет добавить больше перегрузок, если вы хотите манипуляторы, вы можете использовать write лучше fprintf для атомной записи в деструкторе или std::cerr , вы можете обобщить так, чтобы получатель был передан конструктору (std::ostream/файловый дескриптор/FILE*),

+0

Я думаю, что я бы добавил элемент 'flush', который делает то же самое, что и деструктор, и очищает внутренний буфер. Затем вы можете повторно использовать один и тот же атом многократно, если хотите. Некоторые люди могут предпочесть использование дополнительных областей, как в вашем втором примере. –

+0

@MooingDuck: Не знаю, как идти ... Я понимаю, о чем вы просите, но я обнаружил, что область действия позволяет мне игнорировать содержимое, когда я смотрю на логику, а не на следы (наша система ведения журнала позволяет аналогичные конструкции). То есть при правильном использовании (т. Е. Не смешивать логику с протоколированием) область может использоваться для анализа содержимого и обеспечения отсутствия реальной логики, после чего мне не нужно пытаться интерпретировать, какие внутренние петли делают, если я смотрю на логику всей функции. –

20

Как указывалось другими, в C++ 11, std::coutis поточно-безопасный.

Однако, если вы используете его как

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

с разными потоками, выход по-прежнему могут чередоваться, так как каждый << новый вызов функции, которая может быть предваряться любым вызовом функции на другом потоке.

Чтобы избежать перемежения без#pragma omp critical - что бы заблокировать все - вы можете сделать следующее:

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

Три вызова пишущие 123 в потоке происходит в только один поток на локальный, не -объект, поэтому на него не влияют никакие другие потоки. Затем есть только один вызов общего потока вывода std::cout, где порядок элементов 123 уже исправлен, поэтому не будет запутан.

0

У меня недостаточно репутации, чтобы опубликовать комментарий, но я хотел опубликовать свое дополнение к классу AtomicWriter для поддержки std :: endl и разрешить использовать другие потоки помимо std :: cout. Вот оно:

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