Как я могу создать новый класс для наследования ostream и использовать его как cout, но с блокировкой - PullRequest
0 голосов
/ 03 апреля 2019

Я использую std :: cout для печати журнала на консоли. Поскольку программа является многопоточной, результат печати будет неупорядоченным, если я использую более одной операции «<<» после cout. </p>


Например, если один поток выполняет cout<< "A" << "B" << endl; Другой поток может выполнить cout << "C"; между A и B. Результатом будет "ACB".


Следовательно, я собираюсь написать новый класс для наследования ostream (что на самом деле basic_ostream<char, char_traits<char>>) и добавить блокировку при инициализации cout, поэтому распечатка должна следовать в правильном порядке.

Ответы [ 3 ]

2 голосов
/ 03 апреля 2019

Один из вариантов - создать класс, который содержит ссылку на поток, но удерживает блокировку на протяжении всего времени его существования.Вот простой пример:

#include <iostream>
#include <mutex>

struct LockedOstream {
    std::lock_guard<std::mutex> lg_;
    std::ostream& os_;
    LockedOstream(std::mutex& m, std::ostream& os)
      : lg_{m}
      , os_{os}
    { }
    std::ostream& stream() const { return os_; }
};

int main()
{
    std::mutex m;
    LockedOstream(m, std::cout).stream() << "foo " << "bar\n";
    //                        ^ locked now                   ^ unlocked now
}

Это работает до тех пор, пока все операции печати, формирующие одну «единицу» вывода, выполняются в одном выражении.


Редактировать: На самом деле, версия наследования намного лучше, чем я ожидал:

#include <iostream>
#include <mutex>

class LockedOstream : public std::ostream {
    static std::mutex& getCoutMutex()
        // use a Meyers' singleton for the cout mutex to keep this header-only
    {
        static std::mutex m;
        return m;
    }

    std::lock_guard<std::mutex> lg_;

  public:
    // Generic constructor
    // You need to pass the right mutex to match the stream you want
    LockedOstream(std::mutex& m, std::ostream& os)
      : std::ostream(os.rdbuf())
      , lg_{m}
    { }

    // Cout specific constructor
    // Uses a mutex singleton specific for cout
    LockedOstream()
      : LockedOstream(getCoutMutex(), std::cout)
    { }
};

int main()
{
    LockedOstream() << "foo " << "bar\n";
    //            ^ locked now          ^ unlocked now
}

В целом:

(хотя последний иногда спорный, но по крайней мере это хорошая идея, чтобы знать и сделать осознанный выбор).

2 голосов
/ 03 апреля 2019

Вы можете определить свою собственную функцию

template<typename... Ts>
void locked_print(std::ostream& stream, Ts&&... ts)
{
    static std::mutex mtx;
    std::lock_guard<std::mutex> guard(mtx);
    (stream << ... << std::forward<Ts>(ts));
}

И если вы хотите быть уверенным, что это эксклюзив, вы можете назвать его как locked_print(std::cout, 1, 2, "bar");

1 голос
/ 03 апреля 2019

Поскольку outstream << x1 << x2 << ... являются множественными вызовами функций, вы не можете сделать это внутренне, кроме как заблокировать все до уничтожения этого же потока.Вы можете просто заставить свое ограничение, когда вы называете это:

{
  std::lock_guard<std::mutex> guard(global_mutex);
  // your print here
}
...