Russian Qt Forum

Qt => Вопросы новичков => Тема начата: egorsmkv от Январь 11, 2016, 12:22



Название: Правильное использование многопоточности
Отправлено: egorsmkv от Январь 11, 2016, 12:22
Задача очень простая - распарсить построчно большой лог.

В языке Go существуют легковесные потоки - Gorutines. С их помощью эта задача выполняется довольно быстро.
Но как это сделать в Qt?

Сейчас я создаю список QFuture и добавляю в него задачи парсинга:
Код:
QString line = "hello";
futureList << QtConcurrent::run(this, &ParserThread::parseLine, line);

При таком подходе всё работает, но для не слишком больших логов. Если дать лог на более чем 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 запуск/остановку/возобновление/остановку потоков. Вот проект, кому интересно - https://yadi.sk/d/qUh0x_rXnprSV https://yadi.sk/d/US55QXNJnr6am


Название: 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
Там всё довольно тривиально реализовано. Покритикуйте хотя-бы то, что есть.
Здесь нужно захватить мутекс
Код
C++ (Qt)
void Worker::resumeThreads() {
//  QMutexLocker lock(&sync);
 pause = 0;
 cond.wakeAll();
}
 
Вообще QWaitCondition здесь не очень лепится, обычно он связывается с выемкой задач из очереди, но тут это уже делает Qt


Название: Re: Правильное использование многопоточности
Отправлено: egorsmkv от Январь 29, 2016, 16:55
Там всё довольно тривиально реализовано. Покритикуйте хотя-бы то, что есть.
Здесь нужно захватить мутекс
Код
C++ (Qt)
void Worker::resumeThreads() {
//  QMutexLocker lock(&sync);
 pause = 0;
 cond.wakeAll();
}
 
Вообще 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?
Да, иначе
Код
C++ (Qt)
void Worker::task() {
 forever {
   // pause block
   sync.lock();
   if (pause.load()) {
     // <- пробой здесь
     qDebug() << "Task[" << QThread::currentThreadId() << "] paused";
     cond.wait(&sync);
   }
   sync.unlock();
 

Вместо 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
}
Эдак Вы напрасно жжете процессор. Возможно Вы имели ввиду
Код
C++ (Qt)
 
while (pause)
 msleep(delay_,sec);
 
Не очень аккуратно - зато очень просто  :)


Название: 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 условной переменной.
Шо тут понимать  ???
Код
C++ (Qt)
void Worker::task() {
 forever {
   // pause block
   sync.lock();
   ...
   cond.wait(&sync);
Все рабочие нитки будут пытаться захватить один и тот же мутекс - и на каждом расчете. Это очень плохо


Название: Re: Правильное использование многопоточности
Отправлено: Old от Январь 31, 2016, 12:57
Вы про шутейный пример с паузой?
Да, будет честно работать мьютиксы, но, по мне, это лучше чем атомики и sleep. Тогда уж атомики и yield.


Название: Re: Правильное использование многопоточности
Отправлено: egorsmkv от Март 23, 2016, 14:12
Вот архив проекта с github


Название: Re: Правильное использование многопоточности
Отправлено: vebmaster от Февраль 02, 2017, 17:30
Вот архив проекта с github
Работает, спасибо!