На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: JoeUser, Qraizer, Hsilgos
Страницы: (4) 1 2 [3] 4  все  ( Перейти к последнему сообщению )  
> Трабла с манипулятором, Вызывается не то и не там :(
Цитата Олег М @
Лучше эту задачу решать так - есть однопоточный класс Message, с оператором <<, который форматирует сообщение в какой-нибудь stringstream, и есть Logger, который под блокировкой сохраняет Message в файл:

Хм, интересный подход. Надо будет попробовать.
Цитата Wound @
Так нельзя делать. Это ограничение.

В смысле? :blink: Я тебе указал на 40 строку твоего кода и указал что там запись в память ни чем не проверяется. А вдруг лимит памяти уже настал? ... Но ты мне отвечаешь "Так нельзя делать. Это ограничение". Что нельзя делать? И где/что за ограничение?
Цитата Wound @
В каком смысле бесконтрольное?

Цитата Wound @
Не совсем понял. Что должно проверятся куда?

нет чека этого:
Errors are signaled by modifying the internal state flags, except for (3), that never sets any flags (but the particular manipulator applied may) ...
или этого:
If the operation sets an internal state flag that was registered with member exceptions, the function throws an exception of member type failure

Цитата Wound @
Это не окончательная версия, это тестовый пример

Ну тогда - сорь. Но проверку на ошибки не забудь!
Мои программные ништякиhttp://majestio.info
Цитата JoeUser @
В смысле? Я тебе указал на 40 строку твоего кода и указал что там запись в память ни чем не проверяется. А вдруг лимит памяти уже настал? ... Но ты мне отвечаешь "Так нельзя делать. Это ограничение". Что нельзя делать? И где/что за ограничение?

Ограничение вообще писал я про то, что нельзя без endl вызывать. А про память - это в данном случае не имеет значение, это тестовый класс, и проверять в нем лимит памяти смысла просто нет.
Цитата Wound @
Ограничение вообще писал я про то, что нельзя без endl вызывать. А про память - это в данном случае не имеет значение, это тестовый класс, и проверять в нем лимит памяти смысла просто нет.

Оке, проехали :)

И все ж - глянь мой пример. Я думаю, можно не плодить кучу классов. Если в моем 'LoggerStream' добавить средства синхронизации - то он может "помогать" работать асинхронно любому потомку от std::ostream. Но я бы не трогал стримы в плане организации их эффективного кэширования - а завел бы отдельный класс, который был бы манагером кэширования. Считаю такой подход - рассово верным! :)

Скрытый текст
ЗЫ: Может быть поможет тебе где-то как-то в борьбе с стримами :lol:

ExpandedWrap disabled
    #include <type_traits>
    #include <utility>
    #include <iostream>
    #include <sstream>
     
    template<typename S, typename T>
    class is_streamable {
      private:
        template<typename SS, typename TT>
        static auto test(int) -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
        template<typename, typename>
        static auto test(...) -> std::false_type;
      public:
        static const bool value = decltype(test<S,T>(0))::value;
    };
     
    class C {};
     
    int main() {
      std::cout << is_streamable<std::stringstream, C>::value << std::endl;
      std::cout << is_streamable<std::stringstream, int>::value << std::endl;
      return 0;
    }
Мои программные ништякиhttp://majestio.info
Цитата JoeUser @
Если в моем 'LoggerStream' добавить средства синхронизации - то он может "помогать" работать асинхронно любому потомку от std::ostream. Но я бы не трогал стримы в плане организации их эффективного кэширования - а завел бы отдельный класс, который был бы манагером кэширования.

Так у вас все пишется в файл сразу, а у меня прога и так очень активно с файлами работает, в прошлой теме же наговорили, что тупить будет, если одновременно в нескольких потоках работать с разными файлами, вот я и делаю логгер, чтоб он минимально работал с файлами, по умолчанию кеш у меня - это потокобезопасная очередь std::wstring'ов. В нее и пишу, а поток используется исключительно и только для формирования 1 строчки лога, потому как это удобнее, чем писать всякие извращения типа itoa, strcat и им подобные.
В данной теме, мне пока больше всего понравился подход Олег М. Там он простой и безопасный + нет кучи мьютексов и остального треша. Пока решил остановится на его задумке, темболее это здорово сокращает количество кода.
Сообщение отредактировано: Wound -
Цитата Wound @
Так у вас все пишется в файл сразу

Так можно с буфером I/O поиграться:

ExpandedWrap disabled
    #include <fstream>
    int main () {
      const size_t bufsize = 1024*1024;
      char *buff = new char[bufsize];
      std::ofstream ofs("test.txt", std::ofstream::out);
      ofs.rdbuf()->pubsetbuf(buff, bufsize);
      ofs << "Hello, buffered stream!\n";
      return 0;
    }


Добавлено
ЗЫ: Хотя да, надо будет как-то экспериментальным способом определять - когда пользовать "\n", а когда std::endl.
От этого зависит момент сброс буфера.
Мои программные ништякиhttp://majestio.info
Цитата JoeUser @
Так можно с буфером I/O поиграться:

Зачем? Твой пример - из конца 90х - начала 2000х, это как минимум небезопасный и морально устаревший код. С кучей подводных камней.
Я вообще считаю что за raw pointer'ы по рукам нужно бить, а уж за new - темболее.
Плюс ко всему нужно вставлять ненужные проверки на то что хватило ли буфера и все это еще оборачивать мьютексами, имхо это очень опасный код ты написал для многопоточной среды.
У меня же по сути используется такой же буфер, но безопасный в плане ошибок и многопоточный.

Добавлено
У меня же по сути сбрасывается в файл примерно так:
ExpandedWrap disabled
     std::ofstream m_fout; //член класса Logger
     
    Logger::Logger
    {
       m_fout.open(filename)
    }
     
    Logger::~Logger
    {
       m_fout.close()
    }
     
    void flush()
    {
       while(queue.size() > 0)
       {
          std::wstring message;
          if(queue.try_pop(message()))
             fout << message; // <<<<<< тут фактически выводятся данные в файловый поток
       }
       fout.flush(); //! <<<<< вот тут фактически осуществляется запись в файл
    }
Сообщение отредактировано: Wound -
Цитата Wound @
Зачем? Твой пример - из конца 90х - начала 2000х, это как минимум небезопасный и морально устаревший код.

Ноу комментс :lol: Киля, ну не нравится мой код - в доку хоть загляни, почитай как там описана работа буферизации. А то делаешь поспешные выводы не разобравшись.

Добавлено
ЗЫ: Хотя ладно, как говорят - на нет и суда нет. Я попытался :lol:
Мои программные ништякиhttp://majestio.info
Цитата JoeUser @
Ноу комментс Киля, ну не нравится мой код - в доку хоть загляни, почитай как там описана работа буферизации. А то делаешь поспешные выводы не разобравшись.

Ок, давай перефразирую: так а смысл мне писать из разных потоков в один файл? Зачем мне так делать? Какой профит я получу от этого? Я этого не понимаю.
Плюс ко всему код что ты привел - очень опасный для многопоточной среды.
У тебя получается что std::ofstream ofs("test.txt", std::ofstream::out); нужно защищать мьютексом. Зачем ?
Сейчас у меня локальная переменная std::wostingstream - для каждого потока, они не конкурируют за этот поток(std::wostingstream), а каждый пишет в свой поток, а потом сбрасывают это содержимое в общуюю очередь, и им плевать что там дальше происходит с этой очередью, об этом заботится кто то другой. И работа с файлом идет ровно в одном потоке.
Как по мне это дает больше гарантий целосности данных + больший контроль над состоянием файла. Разве я ошибаюсь?
Сообщение отредактировано: Wound -
Цитата Wound @
Ок, давай перефразирую: так а смысл мне писать из разных потоков в один файл? Зачем мне так делать? Какой профит я получу от этого? Я этого не понимаю.

Ланна, объясню по шагам, как я это понимаю.

  • Максимальную скорость записи можно получить при записи в один поток и один файл (диск по минимуму дергает позиционирование считывающих головок, если он не SDD)
  • Библиотечная буферизация работает отлично, не надо ничего контролить, переполнений не будет
  • Если увеличить буфер для потока, допустим до 8 или 16 mb, мелкие строки будут влетать в поток только в путь, без фактической записи на диск
  • Размер буфера я бы считал исходя из аппаратного буфера HDD, чтобы чисто скинули нужный объем, и HDD "сказал" сразу мол "я записал" ... а сам еще пишет
  • При наличии достаточного размера буфера - ты из разных threads, по факту, будешь писать в буфер потока, а не сразу в файл
  • Использование средств синхронизации обеспечит безопасное использование потоков при многопоточном способе доступа к ним

Чисто ИМХО. Идеальным моментом сброса буфера (flush) будет момент, когда буфер заполнится на 3/4 или 5/7 от объема HDD. Естественно, этот функционал нужно по-хорошему реализовать в твоем логере. Произвести тестовые испытания.

Я поэтому и писал выше:

Цитата JoeUser @
Но я бы не трогал стримы в плане организации их эффективного кэширования - а завел бы отдельный класс, который был бы манагером кэширования.


Ну вот как-то так.

Добавлено
Цитата Wound @
Плюс ко всему код что ты привел - очень опасный для многопоточной среды.
У тебя получается что std::ofstream ofs("test.txt", std::ofstream::out); нужно защищать мьютексом. Зачем ?


Обязательно нужно!
Многопоточный доступ к стримам без ошибок Стандарт С++ не гарантирует.
Пока почитать негде - Qraizer'а попроси, если нужен железный пруф.

Добавлено
Повторю свой код и поставлю комменты по синхронизации:
ExpandedWrap disabled
        #include <iostream>
        #include <fstream>
        
        using namespace std;
        
        class LoggedStream {
          private:
            ostream& out;
          public:
            LoggedStream(ostream& o):out(o){}
            template<typename T>
            const LoggedStream& operator<<(const T& v) const {
              // начало синхронизации
              out << v;
              // завершение синхронизации
              return *this;
            }
            LoggedStream const& operator<<(std::ostream& (*func)(std::ostream&)) const {
              // начало синхронизации
              func(out);
              // завершение синхронизации
              return *this;
            }
        };
        
        int main(int,char**) {
          LoggedStream Log1(std::cout);
          Log1 << 1 << " 2" << endl << 3 << " 4" << endl;
          //
          std::ofstream ofs("test.txt", std::ofstream::out);
          LoggedStream Log2(ofs);
          Log2 << 1 << " 2" << endl << 3 << " 4" << endl;
        }
Мои программные ништякиhttp://majestio.info
Цитата JoeUser @
Максимальную скорость записи можно получить при записи в один поток и один файл (диск по минимуму дергает позиционирование считывающих головок, если он не SDD)

Так мне не нужна максимальная скорость записи. Это же лог файл, туда данные будут скидываться по мере необходимости.

Цитата JoeUser @
Обязательно нужно!
Многопоточный доступ к стримам без ошибок Стандарт С++ не гарантирует.

Это я знаю. Это же было утверждение, а не вопрос. Вопрос был - Зачем мне это? Там перед "Зачем?" - точка стоит.

Цитата JoeUser @
Повторю свой код и поставлю комменты по синхронизации:
ExpandedWrap disabled
    ...
    const LoggedStream& operator<<(const T& v) const {
              // начало синхронизации
              out << v;
              // завершение синхронизации
              return *this;
            }
            LoggedStream const& operator<<(std::ostream& (*func)(std::ostream&)) const {
              // начало синхронизации
              func(out);
              // завершение синхронизации
              return *this;
            }
    ...


У тебя тут уже ошибка, ты получил race condition. А мне это зачем? Понимаешь - именно по этому я и считаю, что плохая идея работать с одним файлом из разных потоков. Именно по этой причине - я тут изгалялся с condition variable внутри манипулятора. А ты совершил ровно ту же ошибку, которую я тут пытаюсь разрешить. Поэтому мне видится код Олег М более приемлемым. Потому как там не возникает состояния гонки потоков. А потом сформированное сообщение безопасно помещается в потокобезопасную очередь - читай кэш.
И по достижении определенного размера кэш сбрасывается в файл на диск.
Цитата Wound @
Потому как там не возникает состояния гонки потоков.

Походу ты малеха путаешь термин "гонка". Гонки не будет.
Будет конкуренция, и это в многопоточном приложении - нормально.

Добавлено
Хотя что это я ... чуть позже напишу работающий код, освобожусь немного только.
Мои программные ништякиhttp://majestio.info
Цитата JoeUser @
Походу ты малеха путаешь термин "гонка". Гонки не будет.
Будет конкуренция, и это в многопоточном приложении - нормально.

Да нет, не путаю:
Состояние гонки
Цитата
Состояние гонки (англ. race condition), также конкуренция[1

Дело в том, что я еще не понял - понял ли ты сам свою ошибку или нет. Но, я на всякий случай тебе объясню:
На самом деле у тебя твой пример будет работать не так, как ты думаешь, вот тут:
ExpandedWrap disabled
    LoggedStream Log1(std::cout);
     
          Log1 << 1 << " 2" << endl << 3 << " 4" << endl; //! <<<<<<< Вот в этой строчке

На самом деле у тебя аж 6(!) вызовов разных методов класса LoggedStream, и вызовы эти не последовательны, а хаотичны.
В итоге если ты эту строчку напишешь в разных потоках, то получишь не две строчки:
Цитата thread1
1 2
3 4

Цитата thread2
1 2
3 4

А что то совсем другое. Как пример:
Цитата thread1
1 1
4 2

Цитата thread2
2 3
3 4

А это наверное не то, что ты ожидал. У тебя несколько потоков будут в состоянии гонки за переменную std::cout.
И это не нормально.
Щас я твой пример перепишу чтоб он работал, покажу на наглядном примере.
Сообщение отредактировано: Wound -
Wound, все понял. Писать не надо.
Да, ты прав - гонка будет в случае более одного '<<'.
Мои программные ништякиhttp://majestio.info
Вот попробуй выполнить у себя этот код.
ExpandedWrap disabled
    #include <iostream>
    #include <mutex>
    #include <thread>
    #include <chrono>
     
    using namespace std::chrono_literals;
     
    class LoggedStream {
    private:
        std::ostream& out;
        mutable std::mutex m_mutex;
    public:
        LoggedStream(std::ostream& o) : out(o) {}
        template<typename T>
        const LoggedStream& operator<<(const T& v) const {
            std::lock_guard<std::mutex> guard(m_mutex); //! Начало синхронизации, завершение синхронизации в деструкторе, после выхода из этой функции!
            // начало синхронизации
            out << v;
            // завершение синхронизации
            return *this;
        }
        LoggedStream const& operator<<(std::ostream& (*func)(std::ostream&)) const {
            std::lock_guard<std::mutex> guard(m_mutex); //! Начало синхронизации, завершение синхронизации в деструкторе, после выхода из этой функции!
            // начало синхронизации
            func(out);
            // завершение синхронизации
            return *this;
        }
    };
     
    void task1(LoggedStream& stream)
    {
        auto old = std::chrono::steady_clock::now();
        while (true)
        {
            auto now = std::chrono::steady_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - old).count();
            if (elapsed > 3)
                break;
            stream << "The " << " quick " << " brown " << " fox " << " jumps " << " over " << " the " << " lazy " << " dog " << std::endl;
        }
    }
     
    void task2(LoggedStream& stream)
    {
        auto old = std::chrono::steady_clock::now();
        while (true)
        {
            auto now = std::chrono::steady_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - old).count();
            if (elapsed > 3)
                break;
            stream << "This " << " is " << " message " << " from " << " the " << " second " << " thread " << " olo" << "lo " << std::endl;
        }
    }
     
    int main()
    {
        LoggedStream Log1(std::cout);
     
        std::thread tr1(task1, std::ref(Log1));
        std::thread tr2(task2, std::ref(Log1));
     
        tr1.join();
        tr2.join();
     
        std::cin.get();
        return 0;
    }

Допустим у меня на компиле, как вариант, я получил:
Цитата

The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox This is message from the second thread ololo
jumps over the lazy dog
This is message from the second thread ololo
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
This is message from the second thread ololo
The quick brown fox jumps over the lazy dog
This is message from the second thread ololo
The quick brown fox jumps over the lazy dog
This is message from the second thread ololo
The quick brown fox jumps over This is message from the second thread
ololo
the lazy dog
This is message from the second thread ololo
The quick brown fox jumps over the lazy dog
This is message from the second thread ololo
This is message from the second thread ololo
The quick brown fox jumps over the lazy dog
This is message from the The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog

Это ли не race condition ? :-?
1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
0 пользователей:
Страницы: (4) 1 2 [3] 4  все


Рейтинг@Mail.ru
[ Script Execution time: 0,1737 ]   [ 20 queries used ]   [ Generated: 21.07.18, 09:23 GMT ]