Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.223.119.17] |
|
Страницы: (4) 1 2 [3] 4 все ( Перейти к последнему сообщению ) |
Сообщ.
#31
,
|
|
|
Цитата Олег М @ Лучше эту задачу решать так - есть однопоточный класс Message, с оператором <<, который форматирует сообщение в какой-нибудь stringstream, и есть Logger, который под блокировкой сохраняет Message в файл: Хм, интересный подход. Надо будет попробовать. |
Сообщ.
#32
,
|
|
|
В смысле? Я тебе указал на 40 строку твоего кода и указал что там запись в память ни чем не проверяется. А вдруг лимит памяти уже настал? ... Но ты мне отвечаешь "Так нельзя делать. Это ограничение". Что нельзя делать? И где/что за ограничение? нет чека этого: 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 Ну тогда - сорь. Но проверку на ошибки не забудь! |
Сообщ.
#33
,
|
|
|
Цитата JoeUser @ В смысле? Я тебе указал на 40 строку твоего кода и указал что там запись в память ни чем не проверяется. А вдруг лимит памяти уже настал? ... Но ты мне отвечаешь "Так нельзя делать. Это ограничение". Что нельзя делать? И где/что за ограничение? Ограничение вообще писал я про то, что нельзя без endl вызывать. А про память - это в данном случае не имеет значение, это тестовый класс, и проверять в нем лимит памяти смысла просто нет. |
Сообщ.
#34
,
|
|
|
Цитата Wound @ Ограничение вообще писал я про то, что нельзя без endl вызывать. А про память - это в данном случае не имеет значение, это тестовый класс, и проверять в нем лимит памяти смысла просто нет. Оке, проехали И все ж - глянь мой пример. Я думаю, можно не плодить кучу классов. Если в моем 'LoggerStream' добавить средства синхронизации - то он может "помогать" работать асинхронно любому потомку от std::ostream. Но я бы не трогал стримы в плане организации их эффективного кэширования - а завел бы отдельный класс, который был бы манагером кэширования. Считаю такой подход - рассово верным! Скрытый текст ЗЫ: Может быть поможет тебе где-то как-то в борьбе с стримами #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; } |
Сообщ.
#35
,
|
|
|
Цитата JoeUser @ Если в моем 'LoggerStream' добавить средства синхронизации - то он может "помогать" работать асинхронно любому потомку от std::ostream. Но я бы не трогал стримы в плане организации их эффективного кэширования - а завел бы отдельный класс, который был бы манагером кэширования. Так у вас все пишется в файл сразу, а у меня прога и так очень активно с файлами работает, в прошлой теме же наговорили, что тупить будет, если одновременно в нескольких потоках работать с разными файлами, вот я и делаю логгер, чтоб он минимально работал с файлами, по умолчанию кеш у меня - это потокобезопасная очередь std::wstring'ов. В нее и пишу, а поток используется исключительно и только для формирования 1 строчки лога, потому как это удобнее, чем писать всякие извращения типа itoa, strcat и им подобные. В данной теме, мне пока больше всего понравился подход Олег М. Там он простой и безопасный + нет кучи мьютексов и остального треша. Пока решил остановится на его задумке, темболее это здорово сокращает количество кода. |
Сообщ.
#36
,
|
|
|
Цитата Wound @ Так у вас все пишется в файл сразу Так можно с буфером I/O поиграться: #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. От этого зависит момент сброс буфера. |
Сообщ.
#37
,
|
|
|
Цитата JoeUser @ Так можно с буфером I/O поиграться: Зачем? Твой пример - из конца 90х - начала 2000х, это как минимум небезопасный и морально устаревший код. С кучей подводных камней. Я вообще считаю что за raw pointer'ы по рукам нужно бить, а уж за new - темболее. Плюс ко всему нужно вставлять ненужные проверки на то что хватило ли буфера и все это еще оборачивать мьютексами, имхо это очень опасный код ты написал для многопоточной среды. У меня же по сути используется такой же буфер, но безопасный в плане ошибок и многопоточный. Добавлено У меня же по сути сбрасывается в файл примерно так: 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(); //! <<<<< вот тут фактически осуществляется запись в файл } |
Сообщ.
#38
,
|
|
|
Цитата Wound @ Зачем? Твой пример - из конца 90х - начала 2000х, это как минимум небезопасный и морально устаревший код. Ноу комментс Киля, ну не нравится мой код - в доку хоть загляни, почитай как там описана работа буферизации. А то делаешь поспешные выводы не разобравшись. Добавлено ЗЫ: Хотя ладно, как говорят - на нет и суда нет. Я попытался |
Сообщ.
#39
,
|
|
|
Цитата JoeUser @ Ноу комментс Киля, ну не нравится мой код - в доку хоть загляни, почитай как там описана работа буферизации. А то делаешь поспешные выводы не разобравшись. Ок, давай перефразирую: так а смысл мне писать из разных потоков в один файл? Зачем мне так делать? Какой профит я получу от этого? Я этого не понимаю. Плюс ко всему код что ты привел - очень опасный для многопоточной среды. У тебя получается что std::ofstream ofs("test.txt", std::ofstream::out); нужно защищать мьютексом. Зачем ? Сейчас у меня локальная переменная std::wostingstream - для каждого потока, они не конкурируют за этот поток(std::wostingstream), а каждый пишет в свой поток, а потом сбрасывают это содержимое в общуюю очередь, и им плевать что там дальше происходит с этой очередью, об этом заботится кто то другой. И работа с файлом идет ровно в одном потоке. Как по мне это дает больше гарантий целосности данных + больший контроль над состоянием файла. Разве я ошибаюсь? |
Сообщ.
#40
,
|
|
|
Цитата Wound @ Ок, давай перефразирую: так а смысл мне писать из разных потоков в один файл? Зачем мне так делать? Какой профит я получу от этого? Я этого не понимаю. Ланна, объясню по шагам, как я это понимаю. Чисто ИМХО. Идеальным моментом сброса буфера (flush) будет момент, когда буфер заполнится на 3/4 или 5/7 от объема HDD. Естественно, этот функционал нужно по-хорошему реализовать в твоем логере. Произвести тестовые испытания. Я поэтому и писал выше: Цитата JoeUser @ Но я бы не трогал стримы в плане организации их эффективного кэширования - а завел бы отдельный класс, который был бы манагером кэширования. Ну вот как-то так. Добавлено Цитата Wound @ Плюс ко всему код что ты привел - очень опасный для многопоточной среды. У тебя получается что std::ofstream ofs("test.txt", std::ofstream::out); нужно защищать мьютексом. Зачем ? Обязательно нужно! Многопоточный доступ к стримам без ошибок Стандарт С++ не гарантирует. Пока почитать негде - Qraizer'а попроси, если нужен железный пруф. Добавлено Повторю свой код и поставлю комменты по синхронизации: #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; } |
Сообщ.
#41
,
|
|
|
Цитата JoeUser @ Максимальную скорость записи можно получить при записи в один поток и один файл (диск по минимуму дергает позиционирование считывающих головок, если он не SDD) Так мне не нужна максимальная скорость записи. Это же лог файл, туда данные будут скидываться по мере необходимости. Цитата JoeUser @ Обязательно нужно! Многопоточный доступ к стримам без ошибок Стандарт С++ не гарантирует. Это я знаю. Это же было утверждение, а не вопрос. Вопрос был - Зачем мне это? Там перед "Зачем?" - точка стоит. Цитата JoeUser @ Повторю свой код и поставлю комменты по синхронизации: ... 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 внутри манипулятора. А ты совершил ровно ту же ошибку, которую я тут пытаюсь разрешить. Поэтому мне видится код Олег М более приемлемым. Потому как там не возникает состояния гонки потоков. А потом сформированное сообщение безопасно помещается в потокобезопасную очередь - читай кэш. И по достижении определенного размера кэш сбрасывается в файл на диск. |
Сообщ.
#42
,
|
|
|
Цитата Wound @ Потому как там не возникает состояния гонки потоков. Походу ты малеха путаешь термин "гонка". Гонки не будет. Будет конкуренция, и это в многопоточном приложении - нормально. Добавлено Хотя что это я ... чуть позже напишу работающий код, освобожусь немного только. |
Сообщ.
#43
,
|
|
|
Цитата JoeUser @ Походу ты малеха путаешь термин "гонка". Гонки не будет. Будет конкуренция, и это в многопоточном приложении - нормально. Да нет, не путаю: Состояние гонки Цитата Состояние гонки (англ. race condition), также конкуренция[1 Дело в том, что я еще не понял - понял ли ты сам свою ошибку или нет. Но, я на всякий случай тебе объясню: На самом деле у тебя твой пример будет работать не так, как ты думаешь, вот тут: 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. И это не нормально. Щас я твой пример перепишу чтоб он работал, покажу на наглядном примере. |
Сообщ.
#44
,
|
|
|
Wound, все понял. Писать не надо.
Да, ты прав - гонка будет в случае более одного '<<'. |
Сообщ.
#45
,
|
|
|
Вот попробуй выполнить у себя этот код.
#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 ? |