Название: Правильное использование многопоточности Отправлено: egorsmkv от Январь 11, 2016, 12:22 Задача очень простая - распарсить построчно большой лог.
В языке Go существуют легковесные потоки - Gorutines. С их помощью эта задача выполняется довольно быстро. Но как это сделать в Qt? Сейчас я создаю список QFuture и добавляю в него задачи парсинга: Код: QString line = "hello"; При таком подходе всё работает, но для не слишком больших логов. Если дать лог на более чем 1 миллион строк, то программа начинает подвисать из-за того, что задачи добавляются в futureList. В какую сторону мне смотреть, чтобы, так сказать, плавно их ставить в очередь на парсинг? Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 11, 2016, 12:48 Необязательно, но вполне возможно Вы столкнулись с типовой проблемой "кластер слишком мал". Попробуйте объединять строки в пачки напр по 100 (или 1000, подберете), и их уже скармливать
Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 11, 2016, 12:57 Да, тоже думал, что машина просто не справляется, но о таком решении не догадался. Сейчас буду тестировать.
Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 11, 2016, 15:14 Потребление памяти значительно снизилось из-за уменьшения создания новых QFuture.
Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 11, 2016, 15:54 И того: проблема осталась - программа подвисает.
Попробую сделать через QThreadPool и QRunnable. По результатам отпишусь. Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 11, 2016, 16:39 И того: проблема осталась - программа подвисает. Может лучше разобраться почему/где подвисает.Попробую сделать через QThreadPool и QRunnable. По результатам отпишусь. Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 11, 2016, 19:09 У меня после парсинга строки идёт её запись на диск. Уверен, что из-за этого программа начитает подвисать.
Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 11, 2016, 20:30 У меня после парсинга строки идёт её запись на диск. Уверен, что из-за этого программа начитает подвисать. Порядок строк в выходном файле не важен? Он может отличатся от исходного файла.Можно использовать одну нить для чтения строк из исходного файла и заполнения очереди для обработки, несколько рабочих ниток для распарсивания и заполнения очереди для сохранения и нитка для сохранения результирующих строк. Чтение и запись буферизированные, т.е. прочитали большой кусок в память - побежали разбивать на строки, и формируем буфер для записи в памяти с результирующими строками и запись их одной операцией. Или разделить исходный файл на несколько кусков, по количеству рабочих потоков, и отдать каждый кусок своему потоку. А поток уже будет вычитывать строку из своего куска, парсить ее и сохранять результат. Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 12, 2016, 03:27 Не важен.
Второй вариант больше нравится из-за своей простоты. Попробую его тоже сделать. Название: Re: Правильное использование многопоточности Отправлено: Авварон от Январь 12, 2016, 10:11 И того: проблема осталась - программа подвисает. Попробую сделать через QThreadPool и QRunnable. По результатам отпишусь. Футура и есть обертка над QRunnable Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 12, 2016, 12:04 У меня после парсинга строки идёт её запись на диск. Уверен, что из-за этого программа начитает подвисать. Это легко проверить отключив запись. Можно каждой нитке дать свой выходной файл, или использовать мутексы для записи (подход тот же, лочится запись "пачки" строк). Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 27, 2016, 17:25 Докладываю, как и обещал.
Я ошибался говоря, что подвисание окна происходит из-за активной записи на диск (она происходит в другом потоке). Оказалось всё намного прозаичнее: у меня в программе присутствует прогресс-бар, который обновляется, когда парсинг одной строки завершен. Из-за этого программа и подвисала - из-за частого обновления прогресс-бара. P.S. также сделал с QtConcurrent запуск/остановку/возобновление/остановку потоков. Вот проект, кому интересно - Название: Re: Правильное использование многопоточности Отправлено: fdfsdfgjfh от Январь 27, 2016, 22:59 ссылка битая
Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 28, 2016, 02:22 Исправил
Название: Re: Правильное использование многопоточности Отправлено: gil9red от Январь 28, 2016, 07:34 Исправил Освойте https://github.com/ / https://bitbucket.org/ -- проще же будет проекты хранить, иметь к ним доступ, тут и знания о контролях версий появятся, а они в it-компаниях всегда используются, да и не нужно будет перезаливать :) Не раз было, что в некоторых статья на хабре можно было встретить, выложенный автором проект на файлообменник, ссылка которого, конечно, давно дохлая Название: Re: Правильное использование многопоточности Отправлено: __Heaven__ от Январь 28, 2016, 09:25 А вот интересен вопрос реализации и прироста скорости.
У меня имеется подобная задачка, отложенная на потом. Как эффективнее использовать потоки? Я полагаю, что должен быть 1 поток, который будет читать данные в QStringList, тем самым формируя очередь для парсинга. В это время другие потоки начинают парсить полученные данные в QList результатов. Причём потоков для парсинга должно соответствовать QThread::idealThreadCount(). Как считаете? Жизнеспособно? Прирост скорости получится значительный? Название: Re: Правильное использование многопоточности Отправлено: Bepec от Январь 28, 2016, 09:32 Тут надо смотреть именно на объем работ. Если простой парсинг, прирост будет незначительный. Если парсинг с созданием структур, выделением памяти и преобразованиями - прирост возможно будет, а мб будет замедление из-за малой нагрузки на поток.
PS как говаривал сенсей, нужно разбирать каждый конкретный случай. Одно условие может поменять всё :) Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 28, 2016, 09:44 1) Выясните сколько % времени уходит чисто на чтение, просто отключите теперешний парсинг. Учтите что эффект будет/возможен только для разницы между "полным" временем и "только чтение"
2) Выясните насколько в данном случае притормаживает удобный но жирный QString. Для чтения попробуйте читать файл как двоичный (raw data), для парсинга - смотрите сами Потом о чем-то уже можно будет говорить Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 28, 2016, 12:59 Исправил Освойте https://github.com/ / https://bitbucket.org/ -- проще же будет проекты хранить, иметь к ним доступ, тут и знания о контролях версий появятся, а они в it-компаниях всегда используются, да и не нужно будет перезаливать :) Не раз было, что в некоторых статья на хабре можно было встретить, выложенный автором проект на файлообменник, ссылка которого, конечно, давно дохлая Я его использую, но не хочу пока туда такой сырой код выкладывать Название: Re: Правильное использование многопоточности Отправлено: __Heaven__ от Январь 28, 2016, 15:46 egorsmkv, заведите отдельную ветку для сырого кода. А потом сольёте со стабильной.
Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 28, 2016, 20:17 Сделал - https://github.com/egorsmkv/qt-multithreading
Название: Re: Правильное использование многопоточности Отправлено: gil9red от Январь 28, 2016, 20:41 Сделал - https://github.com/egorsmkv/qt-multithreading Интересно будет посмотреть :) Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 28, 2016, 21:03 Сделал - https://github.com/egorsmkv/qt-multithreading Интересно будет посмотреть :) Там всё довольно тривиально реализовано. Покритикуйте хотя-бы то, что есть. Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 29, 2016, 10:37 Там всё довольно тривиально реализовано. Покритикуйте хотя-бы то, что есть. Здесь нужно захватить мутекс Код Вообще QWaitCondition здесь не очень лепится, обычно он связывается с выемкой задач из очереди, но тут это уже делает Qt Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 29, 2016, 16:55 Там всё довольно тривиально реализовано. Покритикуйте хотя-бы то, что есть. Здесь нужно захватить мутекс Код Вообще QWaitCondition здесь не очень лепится, обычно он связывается с выемкой задач из очереди, но тут это уже делает Qt Мьютекс для pause? Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 29, 2016, 17:03 Мьютекс для pause? В первую очередь для условной переменной.Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 29, 2016, 17:12 Мьютекс для pause? В первую очередь для условной переменной.Это не простая переменная, а QAtomicInt Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 29, 2016, 17:13 Это не простая переменная, а QAtomicInt Условная переменная это cond.Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 29, 2016, 17:50 Понял.
Вместо QWaitCondition что лучше использовать? Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 29, 2016, 17:54 Вместо QWaitCondition что лучше использовать? Это зависит от того, что хотим получить. :)Если вам нужна эта пауза, то условная переменная для этого подходит. Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 29, 2016, 17:56 В коде он используется для ожидания команды "Возобновить работу потоков":
https://github.com/egorsmkv/qt-multithreading/blob/master/worker.cpp#L28 Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 29, 2016, 17:59 В коде он используется для ожидания команды "Возобновить работу потоков": Я дописал предыдущий пост... Если вам нужна пауза, то условная переменная для этого подходит.https://github.com/egorsmkv/qt-multithreading/blob/master/worker.cpp#L28 Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 30, 2016, 08:32 Мьютекс для pause? Да, иначе Код
Вместо QWaitCondition что лучше использовать? QSemaphoreНазвание: Re: Правильное использование многопоточности Отправлено: Old от Январь 30, 2016, 10:25 QSemaphore IMHO, не очень он для организации паузы подходит.Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Январь 30, 2016, 19:42 Вместо QWaitCondition что лучше использовать? Это зависит от того, что хотим получить. :)Если вам нужна эта пауза, то условная переменная для этого подходит. А ожидание, циклом? Или : forever { if (pause) continue; // .. do work } Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 30, 2016, 19:45 А ожидание, циклом? Вы какое ожидание имеете ввиду - во время паузы? Тогда нитка заснет на wait и будет спать пока состояние флага pause не изменится. Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 31, 2016, 07:07 А ожидание, циклом? Эдак Вы напрасно жжете процессор. Возможно Вы имели ввидуИли : forever { if (pause) continue; // .. do work } Код Не очень аккуратно - зато очень просто :) Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 31, 2016, 10:54 Не очень аккуратно - зато очень просто :) Это "не очень аккуратно" только для лабораторок, а вообще это не приемлемо. :)Название: Re: Правильное использование многопоточности Отправлено: Bepec от Январь 31, 2016, 11:23 Самый просто пример - многопоточное копирование файлов. Захотел пользователь - нажал кнопочку пауза. Захотел - отжал.
И да, других вариантов кроме как слипить поток я не вижу. Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 31, 2016, 11:29 Это "не очень аккуратно" только для лабораторок, а вообще это не приемлемо. :) Да, не очень солидно :) Но все же такое решение намного лучше захвата мутекса в рабочей ф-ции. Это сталкивает рабочие нитки лбами, при малом кластере все сожжется на мутексе. Разве что задача - доли секунды, тогда проходит все что угодно. Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 31, 2016, 11:33 Да, не очень солидно :) Но все же такое решение намного лучше захвата мутекса в рабочей ф-ции. Это сталкивает рабочие нитки лбами, при малом кластере все сожжется на мутексе. Разве что задача - доли секунды, тогда проходит все что угодно. Не очень понял про мутексы в рабочих функциях. При паузе нитка должна спать, например на wait условной переменной.Название: Re: Правильное использование многопоточности Отправлено: Igors от Январь 31, 2016, 12:24 Не очень понял про мутексы в рабочих функциях. При паузе нитка должна спать, например на wait условной переменной. Шо тут понимать ???Код Все рабочие нитки будут пытаться захватить один и тот же мутекс - и на каждом расчете. Это очень плохо Название: Re: Правильное использование многопоточности Отправлено: Old от Январь 31, 2016, 12:57 Вы про шутейный пример с паузой?
Да, будет честно работать мьютиксы, но, по мне, это лучше чем атомики и sleep. Тогда уж атомики и yield. Название: Re: Правильное использование многопоточности Отправлено: egorsmkv от Март 23, 2016, 14:12 Вот архив проекта с github
Название: Re: Правильное использование многопоточности Отправлено: vebmaster от Февраль 02, 2017, 17:30 Вот архив проекта с github Работает, спасибо! |