На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (3) 1 [2] 3  все  ( Перейти к последнему сообщению )  
> Вопрос по работе std::promise и std::shared_future
    Цитата Wound @
    Просто тут фишка в том, что у меня в конструкторе лог открывается, сдается мне, что использование std::thread как члена класса, стартанет поток еще до тела конструктора, что приведет к UB.

    Он стартанёт, когда ты его стартуешь, m_thread = std::thread(&ThreadProc.....), ни раньше ни позже.
      Ок, вот так получилось, вроде работает. Пожалуй на этом и остановлюсь. Надеюсь тут больше нет косяков?
      ExpandedWrap disabled
        //Constructor
            Logger::Logger(const std::wstring& logname, unsigned int cachesize)
            : m_cachesize(cachesize),
              m_logname(logname),
              m_isFinished(false)
            {
                openlog(m_logname);
                m_shadow_flusher = std::thread([this]
                {
                    while (!m_isFinished)
                    {
                        std::unique_lock<std::mutex> lk(m_flushmtx);
                        m_isflush.wait(lk, [this] {return m_log.size() >= m_cachesize || m_isFinished; });
                        this->dump(std::move(m_log));
                    }
                });
            }
         
            void Logger::Flush(std::wstring&& msg)
            {
                std::unique_lock<std::mutex> lk(m_flushmtx);
                m_log.push(msg);
                if (m_log.size() >= m_cachesize)
                    m_isflush.notify_one();
            }
         
            void Logger::dump(Msi::Queue<std::wstring>&& queue)
            {
                try
                {
                    while (queue.size() > 0u)
                    {
                        std::wstring val;
                        if (queue.try_pop(val))
                        {
                            m_fout << L"Trhread [" << std::this_thread::get_id() << L"]: " << val;
                        }
                    }
                    m_fout.flush();
                }
                catch (std::exception& err)
                {
                    std::cerr << "Error: " << err.what() << std::endl;
                }
            }
         
            Logger::~Logger()
            {
                m_isFinished.store(true);
                m_isflush.notify_one();
                m_shadow_flusher.join();
         
                m_fout.close();
            }
        Цитата Wound @
              m_shadow_flusher = std::thread([this]
                {
                    while (!m_isFinished)
                    {
                        std::unique_lock<std::mutex> lk(m_flushmtx);
                        m_isflush.wait(lk, [this] {return m_log.size() >= m_cachesize || m_isFinished; });
                        this->dump(std::move(m_log));
                    }
                });

        1. dump() должен вызываться без блокировки.
        2. m_cachesize вообще больше не нужен. Событие можно взводить на каждый вызов Flush(), это дешёвая операция

        ExpandedWrap disabled
          m_shadow_flusher = std::thread([this]
          {
              while (!m_isFinished)
                  this->dump(WaitMessages());
           
               std::lock_guard lock(m_flushmtx);
               this->dump(std::move(m_log));
          });
           
           
          inline
          auto Logger::WaitMessages()
          {
              std::unique_lock<std::mutex> lk(m_flushmtx);
              m_isflush.wait(lk, [this] {return !m_log.empty() || m_isFinished; });
              return std::move(m_log);
          }


        3. m_isFinished можно сделать просто volatile bool. В деструкторе его нужно взводить под блокировкой
        ExpandedWrap disabled
          {
              std::lock_guard lock(m_flushmtx);
              m_isFinished  = true;
              m_isflush.notify_all();
          }
          m_shadow_flusher.join();
          Цитата Олег М @
          3. m_isFinished можно сделать просто volatile bool. В деструкторе его нужно взводить под блокировкой

          Он у меня как std::atomic_bool
            Цитата Wound @
            Он у меня как std::atomic_bool

            Можно без atomic
              Цитата Олег М @
              Можно без atomic

              Так atomic вроде дешевле блокировки.

              Добавлено
              И мне не совсем понятно зачем тут два раза dump вызывать? Ведь все равно WaitMessage все сделает.
              ExpandedWrap disabled
                m_shadow_flusher = std::thread([this]
                {
                    while (!m_isFinished)
                        this->dump(WaitMessages());
                 
                     std::lock_guard lock(m_flushmtx);
                     this->dump(std::move(m_log));
                });
                Цитата Wound @
                Так atomic вроде дешевле блокировки.


                Начитывается он в любом случае под блокировкой, так что ничего у тебя там не дешевле.

                И, в общем случае, устанавливать такие переменные надо под блокировкой - там две операции =true b notify_all(), их надо синхронизировать с чтением

                Добавлено
                Цитата Wound @
                И мне не совсем понятно зачем тут два раза dump вызывать? Ведь все равно WaitMessage все сделает.


                Да, возможно. На всякиий случай, хуже точно не будет.
                  Цитата Олег М @
                  Начитывается он в любом случае под блокировкой, так что ничего у тебя там не дешевле.

                  И, в общем случае, устанавливать такие переменные надо под блокировкой - там две операции =true b notify_all(), их надо синхронизировать с чтением

                  На сколько я знаю использование std::atomic накладывает ограничение на порядок выполнения операций. Т.е. в данном случае notify_all буквально всегда обязано выполнятся только после присваивания значения atomic переменной. По крайней мере так написано у майерса:
                  Цитата

                  Однако применение s t d : : a t o m i c накладывает ограничения на разрешенные переупорядочения
                  кода, и одно такое ограничение заключается в том, что никакой код, предшествующий
                  в исходном тексте записи переменной s t d : : а t omi с, не может иметь место
                  (или выглядеть таковым для друтих ядер) после нее7. Это означает, что в нашем коде
                  auto imptValue = compute importantVa l ue ( ) ; / / Вычисление значения
                  valAvai l aЫe = true ; 11 Сообщение об этом
                  11 другому потоку
                  компиляторы должны не только сохранять порядок присваиваний i m p t V a l u e
                  и valAva i laЬle, но и генерировать код, который гарантирует, что так же поведет себя
                  и аппаратное обеспечение. В результате объявление v a lAva i l a Ы e как s t d : : at o m i c
                  гарантирует выполнение критичного требования к упорядоченности - что значение
                  imptVal ue должно быть видимо всеми потоками как измененное не позже, чем значение
                  valAva i l aЬle.

                  Единственное - это файл может закрыться раньше, чем dump вызвался, но у нас ведь гарантированно 1 поток?

                  Добавлено
                  Ладно попробую переписать, посмотрю что получится.
                  Сообщение отредактировано: Wound -
                    Цитата Wound @
                    На сколько я знаю использование std::atomic накладывает ограничение на порядок выполнения операция. Т.е. в данном случае notify_all буквально всегда обязано выполнятся только после присваивания значения atomic переменной. По крайней мере так написано у майерса:

                    notify_all в любом случае вызовется после присвоения значения. Здесь, в принципе, можно и без блокировки.
                    Я говорил про общий случай - лучше выставление значении всегда синхронизировать с проверкой условий в потоке. Проблем будет меньше.


                    Цитата Wound @
                    Единственное - это файл может закрыться раньше, чем dump вызвался, но у нас ведь гарантированно 1 поток?

                    Не может. Ты join делаешь перед закрытием файла.
                    Сообщение отредактировано: Олег М -
                      Цитата Олег М @
                      Я говорил про общий случай - лучше выставление значении всегда синхронизировать с проверкой условий в потоке. Проблем будет меньше.

                      А, ну тогда понял.

                      Цитата Олег М @
                      Не может. Ты join делаешь перед закрытием файла.

                      Ну да в принципе.

                      Ну вроде все работает с последними изменениями. Спасибо за помощь Олег М.

                      Добавлено
                      На всякий случай выложу что получилось, может быть кому пригодится:
                      ExpandedWrap disabled
                        #ifndef __LOGGER_H_INCLUDED__
                        #define __LOGGER_H_INCLUDED__
                         
                        #include <sstream>
                        #include <mutex>
                        #include <fstream>
                        #include <memory>
                        #include <atomic>
                        #include <future>
                        #include "MsiQueue.h"
                         
                        namespace Msi
                        {
                           class Logger;
                           class UnsafeLogger
                           {
                           public:
                              UnsafeLogger(Logger& logger);
                              UnsafeLogger(UnsafeLogger&& log);
                              ~UnsafeLogger();
                         
                           public:
                              UnsafeLogger() = delete;
                              UnsafeLogger(const UnsafeLogger& logger) = delete;
                              UnsafeLogger& operator=(UnsafeLogger&&) = delete;
                              UnsafeLogger& operator=(const UnsafeLogger&) = delete;
                         
                           public:
                              template <typename T>
                              UnsafeLogger& operator <<(T &&val);
                         
                              auto& operator <<(std::wostream& (*pfmanip)(std::wostream&))
                              {
                                 m_stream << pfmanip;
                                 return *this;
                              }
                         
                              auto& operator <<(UnsafeLogger& (*pfmanip)(UnsafeLogger&))
                              {
                                 return pfmanip(*this);
                              }
                         
                           public:
                              void Flush() noexcept;
                         
                           private:
                              Logger& m_logger;
                              std::wostringstream m_stream;
                           };
                         
                           class Logger
                           {
                           public:
                              explicit Logger(const std::wstring& logname);
                              ~Logger();
                         
                           public:
                              Logger(const Logger&) = delete;
                              Logger(Logger&&) = delete;
                              Logger& operator=(const Logger&) = delete;
                              Logger& operator=(Logger&&) = delete;
                         
                              UnsafeLogger Log();
                              void Flush(std::wstring&& msg);
                           private:
                              
                              void openlog(const std::wstring& logname);
                              void dump(Msi::Queue<std::wstring>&& queue);
                              inline auto wait_messages()
                              {
                                 std::unique_lock<std::mutex> lk(m_flushmtx);
                                 m_isflush.wait(lk, [this] {return m_log.size() > 0u || m_isFinished.load(); });
                                 return std::move(m_log);
                              }
                         
                           private:
                              std::mutex m_flushmtx;
                              std::wofstream m_fout;
                              std::atomic_bool m_isFinished;
                              Msi::Queue<std::wstring> m_log;
                              std::condition_variable m_isflush;
                              std::thread m_shadow_flusher;
                           };
                         
                           template<typename T>
                           inline UnsafeLogger& UnsafeLogger::operator<<(T&& val)
                           {
                              m_stream << std::forward<T>(val);
                              return *this;
                           }
                         
                           namespace log
                           {
                              UnsafeLogger& info(UnsafeLogger& stream);
                              UnsafeLogger& error(UnsafeLogger& stream);
                              UnsafeLogger& thread(UnsafeLogger& stream);
                              UnsafeLogger& warning(UnsafeLogger& stream);
                           }
                        }
                        #endif // !__LOGGER_H_INCLUDED__

                      ExpandedWrap disabled
                        #include "Logger.h"
                        #include <ctime>
                        #include <thread>
                        #include <future>
                        #include <iostream>
                         
                        namespace Msi
                        {
                           UnsafeLogger::UnsafeLogger(Logger& logger)
                           : m_logger(logger)
                           {
                           }
                         
                           UnsafeLogger::UnsafeLogger(UnsafeLogger&& log)
                           : m_logger(log.m_logger),
                             m_stream(std::move(log.m_stream))
                           {
                           }
                         
                           UnsafeLogger::~UnsafeLogger()
                           {
                              Flush();
                           }
                         
                           void UnsafeLogger::Flush() noexcept
                           {
                              m_stream.flush();
                              m_logger.Flush(std::move(m_stream.str()));
                           }
                         
                           Logger::Logger(const std::wstring& logname)
                           : m_isFinished(false)
                           {
                              openlog(logname);
                              m_shadow_flusher = std::thread([this]
                              {
                                 while (!m_isFinished.load())
                                    dump(wait_messages());
                         
                                 std::lock_guard<std::mutex> lk(m_flushmtx);
                                 dump(std::move(m_log));
                              });
                           }
                         
                           Logger::~Logger()
                           {
                              m_isFinished.store(true);
                              m_isflush.notify_all();
                         
                              m_shadow_flusher.join();
                         
                              m_fout.close();
                           }
                         
                           UnsafeLogger Logger::Log()
                           {
                              return UnsafeLogger(*this);
                           }
                         
                           void Logger::openlog(const std::wstring& logname)
                           {
                              m_fout.open(logname, std::ios_base::out | std::ios_base::trunc);
                              if (!m_fout.is_open())
                                 throw std::runtime_error("Error: can't open log file");
                              auto now = std::chrono::system_clock::now();
                              time_t start = std::chrono::system_clock::to_time_t(now);
                              m_fout << L"Log was created at: " << std::ctime(&start);
                              m_fout << L"============================================" << std::endl;
                           }
                         
                           void Logger::Flush(std::wstring&& msg)
                           {
                              {
                                 std::unique_lock<std::mutex> lk(m_flushmtx);
                                 m_log.push(msg);
                              }
                              m_isflush.notify_all();
                           }
                         
                           void Logger::dump(Msi::Queue<std::wstring>&& queue)
                           {
                              try
                              {
                                 while (queue.size() > 0u)
                                 {
                                    std::wstring val;
                                    if (queue.try_pop(val))
                                    {
                                       m_fout << val;
                                    }
                                 }
                                 m_fout.flush();
                              }
                              catch (std::exception& err)
                              {
                                 std::cerr << "Error: " << err.what() << std::endl;
                              }
                           }
                         
                           namespace log
                           {
                              UnsafeLogger& info(UnsafeLogger& stream)
                              {
                                 return stream << L"[Information]";
                              }
                         
                              UnsafeLogger& warning(UnsafeLogger& stream)
                              {
                                 return stream << L"[Warning]";
                              }
                         
                              UnsafeLogger& thread(UnsafeLogger& stream)
                              {
                                 return stream << L"Thread [" << std::this_thread::get_id() << L"]";
                              }
                         
                              UnsafeLogger& error(UnsafeLogger& stream)
                              {
                                 return stream << L"[Error]";
                              }
                           }
                        }

                      Единственное тут есть два момента, во первых вместо моей очереди, нужно подставить любую другую(например нестандартную concurent_queue) или самому написать. Во вторых тут вызывается - std::ctime(&start); функция, которая в Microsoft компиляторах генерирует варнинг, нужно либо убрать вызов этой функции, либо добавить в препроцессор _CRT_SECURE_NO_WARNINGS
                        Цитата Wound @
                        Единственное тут есть два момента, во первых вместо моей очереди, нужно подставить любую другую(например нестандартную concurent_queue) или самому написать

                        Поставь std::list<std::sringstream>
                          Цитата Олег М @
                          Поставь std::list<std::sringstream>

                          А зачем? Мне просто интересно чем std::stringstream лучше?
                            Уберёшь из основного потока вызов m_stream.str(), какая-никакая экономия. (Точнее, тебе там надо std::wstringstream)
                            А с помощью std::list легко можно организовать пул этих стримов.
                            Сообщение отредактировано: Олег М -
                              Ок.
                              Вот версия с std::wstringstream:
                              ExpandedWrap disabled
                                #ifndef __LOGGER_H_INCLUDED__
                                #define __LOGGER_H_INCLUDED__
                                 
                                #include <sstream>
                                #include <mutex>
                                #include <fstream>
                                #include <memory>
                                #include <atomic>
                                #include <future>
                                #include <list>
                                 
                                namespace core
                                {
                                    class Logger;
                                    class UnsafeLogger
                                    {
                                    public:
                                        UnsafeLogger(Logger& logger);
                                        UnsafeLogger(UnsafeLogger&& log);
                                        ~UnsafeLogger();
                                 
                                    public:
                                        UnsafeLogger() = delete;
                                        UnsafeLogger(const UnsafeLogger& logger) = delete;
                                        UnsafeLogger& operator=(UnsafeLogger&&) = delete;
                                        UnsafeLogger& operator=(const UnsafeLogger&) = delete;
                                 
                                    public:
                                        template <typename T>
                                        UnsafeLogger& operator <<(T &&val);
                                 
                                        auto& operator <<(std::wostream& (*pfmanip)(std::wostream&))
                                        {
                                            m_stream << pfmanip;
                                            return *this;
                                        }
                                 
                                        auto& operator <<(UnsafeLogger& (*pfmanip)(UnsafeLogger&))
                                        {
                                            return pfmanip(*this);
                                        }
                                 
                                    public:
                                        void Flush() noexcept;
                                 
                                    private:
                                        Logger & m_logger;
                                        std::wstringstream m_stream;
                                    };
                                 
                                    class Logger
                                    {
                                    public:
                                        explicit Logger(const std::wstring& logname);
                                        ~Logger();
                                 
                                    public:
                                        Logger(const Logger&) = delete;
                                        Logger(Logger&&) = delete;
                                        Logger& operator=(const Logger&) = delete;
                                        Logger& operator=(Logger&&) = delete;
                                 
                                        UnsafeLogger Log();
                                        void Flush(std::wstringstream&& msg);
                                    private:
                                 
                                        void openlog(const std::wstring& logname);
                                        void dump(std::list<std::wstringstream>&& queue);
                                        inline auto wait_messages()
                                        {
                                            std::unique_lock<std::mutex> lk(m_flushmtx);
                                            m_isflush.wait(lk, [this] {return m_log.size() > 0u || m_isFinished.load(); });
                                            return std::move(m_log);
                                        }
                                 
                                    private:
                                        std::mutex m_flushmtx;
                                        std::wofstream m_fout;
                                        std::atomic_bool m_isFinished;
                                        std::list<std::wstringstream> m_log;
                                        std::condition_variable m_isflush;
                                        std::thread m_shadow_flusher;
                                    };
                                 
                                    template<typename T>
                                    inline UnsafeLogger& UnsafeLogger::operator<<(T&& val)
                                    {
                                        m_stream << std::forward<T>(val);
                                        return *this;
                                    }
                                 
                                    namespace log
                                    {
                                        UnsafeLogger& info(UnsafeLogger& stream);
                                        UnsafeLogger& error(UnsafeLogger& stream);
                                        UnsafeLogger& thread(UnsafeLogger& stream);
                                        UnsafeLogger& warning(UnsafeLogger& stream);
                                    }
                                }
                                #endif // !__LOGGER_H_INCLUDED__

                              ExpandedWrap disabled
                                #include "Logger.h"
                                #include <ctime>
                                #include <thread>
                                #include <future>
                                #include <iostream>
                                 
                                namespace core
                                {
                                    UnsafeLogger::UnsafeLogger(Logger& logger)
                                        : m_logger(logger)
                                    {
                                    }
                                 
                                    UnsafeLogger::UnsafeLogger(UnsafeLogger&& log)
                                        : m_logger(log.m_logger),
                                        m_stream(std::move(log.m_stream))
                                    {
                                    }
                                 
                                    UnsafeLogger::~UnsafeLogger()
                                    {
                                        Flush();
                                    }
                                 
                                    void UnsafeLogger::Flush() noexcept
                                    {
                                        m_stream.flush();
                                        m_logger.Flush(std::move(m_stream));
                                    }
                                 
                                    Logger::Logger(const std::wstring& logname)
                                    : m_isFinished(false)
                                    {
                                        openlog(logname);
                                        m_shadow_flusher = std::thread([this]
                                        {
                                            while (!m_isFinished.load())
                                                dump(wait_messages());
                                 
                                            std::lock_guard<std::mutex> lk(m_flushmtx);
                                            dump(std::move(m_log));
                                        });
                                    }
                                 
                                    Logger::~Logger()
                                    {
                                        m_isFinished.store(true);
                                        m_isflush.notify_all();
                                 
                                        m_shadow_flusher.join();
                                 
                                        m_fout.close();
                                    }
                                 
                                    UnsafeLogger Logger::Log()
                                    {
                                        return UnsafeLogger(*this);
                                    }
                                 
                                    void Logger::openlog(const std::wstring& logname)
                                    {
                                        m_fout.open(logname, std::ios_base::out | std::ios_base::trunc);
                                        if (!m_fout.is_open())
                                            throw std::runtime_error("Error: can't open log file");
                                        auto now = std::chrono::system_clock::now();
                                        time_t start = std::chrono::system_clock::to_time_t(now);
                                        m_fout << L"Log was created at: " << std::ctime(&start);
                                        m_fout << L"============================================" << std::endl;
                                    }
                                 
                                    void Logger::Flush(std::wstringstream&& msg)
                                    {
                                        {
                                            std::unique_lock<std::mutex> lk(m_flushmtx);
                                            m_log.push_back(std::move(msg));
                                        }
                                        m_isflush.notify_all();
                                    }
                                 
                                    void Logger::dump(std::list<std::wstringstream>&& queue)
                                    {
                                        try
                                        {
                                            while (queue.size() > 0u)
                                            {
                                                m_fout << queue.front().str();
                                                queue.pop_front();
                                            }
                                            m_fout.flush();
                                        }
                                        catch (std::exception& err)
                                        {
                                            std::cerr << "Error: " << err.what() << std::endl;
                                        }
                                    }
                                 
                                    namespace log
                                    {
                                        UnsafeLogger& info(UnsafeLogger& stream)
                                        {
                                            return stream << L"[Information]";
                                        }
                                 
                                        UnsafeLogger& warning(UnsafeLogger& stream)
                                        {
                                            return stream << L"[Warning]";
                                        }
                                 
                                        UnsafeLogger& thread(UnsafeLogger& stream)
                                        {
                                            return stream << L"Thread [" << std::this_thread::get_id() << L"]";
                                        }
                                 
                                        UnsafeLogger& error(UnsafeLogger& stream)
                                        {
                                            return stream << L"[Error]";
                                        }
                                    }
                                }
                                Неплохо бы тестовый пример еще.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (3) 1 [2] 3  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0532 ]   [ 17 queries used ]   [ Generated: 28.03.24, 15:52 GMT ]