Russian Qt Forum

Qt => Вопросы новичков => Тема начата: Пантер от Январь 11, 2017, 13:28



Название: Новичкам про потоки
Отправлено: Пантер от Январь 11, 2017, 13:28
Полезные советы:

  • Не используйте потоки
  • Если программа виснет, не используйте потоки, пока не попробуете другие методы
  • Если в вашей программе есть аналоги *sleep или всякие блокирующие waitFor*, у вас скорее всего неправильная архитектура
  • Работайте сокетами ассинхронно через сигналы/слоты (readyRead)
  • Если у вас есть есть "тяжелые" вычисления, посмотрите в сторону QtConcurrent
  • Не наследуйтесь от QThread. Лучше наследоваться от QObject и делать moveToThread, в этом случае вы можете один класс использовать как в отдельном потоке, так и в основном без изменения кода. Плюс, можете использовать пулл потоков
  • Попробуйте использовать QRunnable


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 11, 2017, 13:31
Как насчёт OpenMP директив?


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 11, 2017, 13:35
Как насчёт OpenMP директив?
Подробнее? Это заготовка, которую будем дополнять.


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 11, 2017, 13:46
Если имеется некий долгий цикл пробега по вектору вида
Код
C++ (Qt)
for (int i = 0; i < n; ++i)
То можно перед ним написать
Код
C++ (Qt)
#pragma omp parallel for
тем самым получим расщепление цикла на несколько потоков. Вроде, на максимально возможное, по умолчанию. Правда, тут я столкнулся с проблемой вложенности таких вызовов, то есть когда через параллельный for вызывается функция, которая тоже выполняет параллельный for. До конца пока не разобрался, что там творится.


Название: Re: Новичкам про потоки
Отправлено: m_ax от Январь 11, 2017, 13:57
Я бы ещё создал тему "Вредные советы по программированию для новичков"  в какой-нибудь сатирической форме :) Типа:
1) Запомните раз и навсегда - букварь - Зло! Зло с большой буквы! Не засоряйте свой чердак всяким хламом, чердак не резиновый: Помните, Букварь убивает творческое мышление!
2) Никогда, никогда не используйте готовые решения - всегда изобретайте своё. Скорее всего Ваше решение получится более удобным, правильным и расширяемым и главное, понятным, для всех! К тому же это развивает творческое мышление, а Вы Творец (все умрут, а я грейпфрут!).
3) ...

Ну и т.д.  ;)


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 11, 2017, 14:02
m_ax, ты дерзай, а я прикремплю. ;D


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 11, 2017, 14:13
А что это за асинхронная работа с файлом? Я привык открыть файл через поток и прочитать его. О чём речь?


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 11, 2017, 14:37
А что это за асинхронная работа с файлом? Я привык открыть файл через поток и прочитать его. О чём речь?
Сорри, убрал про файлы. Имелось ввиду все, что наследуется от QIODevice.


Название: Re: Новичкам про потоки
Отправлено: kuzulis от Январь 11, 2017, 16:02
Цитировать
Сорри, убрал про файлы. Имелось ввиду все, что наследуется от QIODevice.

Гы, а файлы (QFile) то наследуются. Но QFile ну никак не асинхронное. :)
А как же использовать QSql без потоков то? И вроде нету sleep и waitFor... но но все-равно тормозит.  :)


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 11, 2017, 16:03
Цитировать
Сорри, убрал про файлы. Имелось ввиду все, что наследуется от QIODevice.

Гы, а файлы то наследуются. Но QFile ну никак не асинхронное. :)
Как сказать, waitForBytesWritten у него как реализовано?


Название: Re: Новичкам про потоки
Отправлено: kuzulis от Январь 11, 2017, 16:05
Там оно (QFile) и без waitFor... тормозит. 


Название: Re: Новичкам про потоки
Отправлено: Igors от Январь 11, 2017, 16:05
1) Запомните раз и навсегда - букварь - Зло! Зло с большой буквы! Не засоряйте свой чердак всяким хламом, чердак не резиновый: Помните, Букварь убивает творческое мышление!
В прошлый раз Вы говорили лучше: "букварь - пойло для лохов"  :)

2) Никогда, никогда не используйте готовые решения - всегда изобретайте своё. Скорее всего Ваше решение получится более удобным, правильным и расширяемым и главное, понятным, для всех! К тому же это развивает творческое мышление, а Вы Творец (все умрут, а я грейпфрут!).
Тоже очень полезный совет (пусть и доведенный до абсурда). Кстати до слова "расширяемость" новичку (или проекту его) еще надо дорасти

По поводу "многопоточности"- есть очень разные уровни ее понимания. Я бы добавил примерно такие советы

- использование относительно низкоуровневых примитивов синхронизации (напр QWaitCondition, QSemaphore) не запрещено, но содержит немало ловушек/подвохов на, казалось бы, пустом месте. Напр wakeAll/One оказывается далеко не всегда "кого-то будит".  Поэтому всегда сначала задумайтесь о более простом способе: run нитки и общение с ней с помощью слот/сигнал.

- первое что попадает под удар в параллельном коде - глобальные переменные. Вообще просто так нельзя читать/писать переменные используемые 2-мя и более нитками (хотя есть исключения).

- засисяться мутексами конечно надо, но не суйте их везде. Так Вы засорите код и быстро устанете. Заодно поинтересуйтесь что такое атомарный (unfair) мутекс

- не воспринимайте рекомендацию "вынести в поток" как лучший/единственный способ, нередко это не так. Вообще не связывайтесь с "разпоточиванием" просто так

Welcome to MP nightmare!


Название: Re: Новичкам про потоки
Отправлено: sergek от Январь 11, 2017, 16:08
Полезные советы:
забыли добавить - аминь!


Название: Re: Новичкам про потоки
Отправлено: kuzulis от Январь 11, 2017, 16:09
Цитировать
Лучше наследоваться от QObject и делать moveToThread
Цитировать
Поэтому всегда сначала задумайтесь о более простом способе: run нитки и общение с ней с помощью слот/сигнал.

Не лучше, т.к. оно (использование сигнал/слотов с moveToThread) "забивает" очередь сообщений (когда "приемник , находящийся в потоке" обрабатывает данные медленнее чем они ему передаются), в отличии от "прямого наследования от QThread".

Везде есть свои нюансы.


Название: Re: Новичкам про потоки
Отправлено: Old от Январь 11, 2017, 16:13
Напр wakeAll/One оказывается далеко не всегда "кого-то будит".
Да ладно. Всегда будит.


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 11, 2017, 16:19
Цитировать
Лучше наследоваться от QObject и делать moveToThread
Цитировать
Поэтому всегда сначала задумайтесь о более простом способе: run нитки и общение с ней с помощью слот/сигнал.

Не лучше, т.к. оно (использование сигнал/слотов с moveToThread) "забивает" очередь сообщений (когда "приемник , находящийся в потоке" обрабатывает данные медленнее чем они ему передаются), в отличии от "прямого наследования от QThread".

Везде есть свои нюансы.
Некорректный пример. В этом случае в поток мувать надо приемник. :)


Название: Re: Новичкам про потоки
Отправлено: kuzulis от Январь 11, 2017, 17:27
Цитировать
В этом случае в поток мувать надо приемник.

Дык я про приемник в потоке и говорю.


Название: Re: Новичкам про потоки
Отправлено: Bepec от Январь 11, 2017, 18:43
Очередная тема - надо делать правильно вот так... Ну, правда можно и вот так... А ещё вот эдак... А вот тут не обойтись без этого... Ну в общем как то так делайте, но точно я вам не скажу :)


Название: Re: Новичкам про потоки
Отправлено: m_ax от Январь 11, 2017, 19:09
m_ax, ты дерзай, а я прикремплю. ;D
Боюсь, запинают  :)

Да и программированием я особо то и не занимаюсь, так уж, чисто балуюсь)  

Цитировать
В прошлый раз Вы говорили лучше: "букварь - пойло для лохов"   :)
Старею, да)

Цитировать
Тоже очень полезный совет (пусть и доведенный до абсурда).
Полезным он будет тогда, когда программист критически подходит к своему велосипедотворчеству. Когда сперва он проанализирует другие решения, их плюсы и минусы. И только после этого, может быть, это будет оправдано.. 


Название: Re: Новичкам про потоки
Отправлено: Old от Январь 11, 2017, 20:08
Полезным он будет тогда, когда программист критически подходит к своему велосипедотворчеству. Когда сперва он проанализирует другие решения, их плюсы и минусы. И только после этого, может быть, это будет оправдано.. 
А что-бы проанализировать другие решения нужно будет разобраться с какими нибудь итераторами? Не-не-не. :)


Название: Re: Новичкам про потоки
Отправлено: m_ax от Январь 11, 2017, 20:52
Цитировать
А что-бы проанализировать другие решения нужно будет разобраться с какими нибудь итераторами? Не-не-не.  :)
Да-да, вот третьим пунктом как раз про итераторы и stl хотел написать, но остановился..  :)
Это как раз другая крайность - инертность к иным (отличным) технологиям и подходам, что сегодня есть здесь и сейчас.. Та противодействующая сила эволюции, что поддерживает видит либо чёрное, либо белое..


Название: Re: Новичкам про потоки
Отправлено: Igors от Январь 12, 2017, 09:16
Да и программированием я особо то и не занимаюсь, так уж, чисто балуюсь)  
Я вижу :)

Полезным он будет тогда, когда программист критически подходит к своему велосипедотворчеству. Когда сперва он проанализирует другие решения, их плюсы и минусы. И только после этого, может быть, это будет оправдано.. 
Все правильно, только есть "цена вопроса". Если я и так это легко напишу - зачем мне изучать какие-то технологии? Если вещь нужна "в году раз" - чего из-за нее бегать с какими-то бустовскими фишками? Это напрасная трата времени и памяти.

Да-да, вот третьим пунктом как раз про итераторы и stl хотел написать, но остановился..  :)
Это как раз другая крайность - инертность к иным (отличным) технологиям и подходам, что сегодня есть здесь и сейчас.. Та противодействующая сила эволюции, что поддерживает видит либо чёрное, либо белое..
Тут я что-то вообще не понял про "другую крайность". Но тоже пофилософствую. Вот есть напр эта Задача (http://www.prog.org.ru/index.php?topic=30273.msg223460#msg223460). Гуглению (анализу готовых решений) была посвящена не одна неделя. Тут уже не до великов, готов изучить хоть 10 дустов если поможет - но увы :'( Вы обладаете по меньшей мере в 10 раз бОльшими знаниями чем я - но Вам эта Задача совершенно неинтересна. Заниматься серьезными делами Вы не хотите, потому что чердак забит всяким хламом. Все эти "итераторы" да "технологии" хороши когда решение-то уже известно, и вопрос только в том как его лучше тиражировать. Но это не всегда так.

Кстати, дешевое решение там есть, правда плохого качества


Название: Re: Новичкам про потоки
Отправлено: Old от Январь 12, 2017, 09:48
Вы обладаете по меньшей мере в 10 раз бОльшими знаниями чем я - но Вам эта Задача совершенно неинтересна.
У вас претензии к m_ax, что он не решает ваши задачи? Требуйте его забанить, нам такие на форуме не нужны. :)


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 12, 2017, 09:51
Пиплы, давайте в этой теме не оффтопить.


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 10:15
Код
C++ (Qt)
Попробуйте использовать QRunnable
А как это работает? Не совсем понятно, чем лучше создания потока.


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 12, 2017, 10:18
Код
C++ (Qt)
Попробуйте использовать QRunnable
А как это работает? Не совсем понятно, чем лучше создания потока.
ты создаешь потомка от QRunnable и засовываешь в пулл. Если у тебя куча мелких задачек, то это хороший вариант.


Название: Re: Новичкам про потоки
Отправлено: Old от Январь 12, 2017, 10:19
А как это работает? Не совсем понятно, чем лучше создания потока.
Это средний  уровень, между QThread и QtConcurrent.
Вы описываете "работу" в классах-наследниках от QRunnable, а потом можете их выполнять в рабочих потоках Кютешного пула потоков QThreadPool.


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 10:30
Получается, если у меня есть класс Solver, то я просто написав run() засовываю его в поток?
Это просто является одним из способов параллелизма, так? Преимущества только в записи?


Название: Re: Новичкам про потоки
Отправлено: Old от Январь 12, 2017, 10:32
Нам не нужно писать свой пул потоков, достаточно описать сами работы.


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 10:34
Спасибо. С пулами не работал. Надо попробовать.


Название: Re: Новичкам про потоки
Отправлено: Old от Январь 12, 2017, 10:49
Спасибо. С пулами не работал. Надо попробовать.
Да проще сразу QtConcurrent использовать. Он базируется на пуле и более высокого уровня.


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 12:04
С этим зверем знаком)))

Хотел бы ещё обсудить такую вещь. При использовании потоков как антифриз для морды, каким образом лучше реализовывать отображение прогресса?
Сейчас я использую примерно следующее: передаю указатель на структуру, где хранится наименование операции, текущий процент выполнения. Диалог прогресса с частотой 100 мс опрашивает эту структуру, а решатель ведёт в неё запись. Интуитивно чувствую, что не самый лучший вариант. Есть предложения?


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 12, 2017, 12:15
Я сигналом реализовывал.


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 12:22
При использовании сигнала приходится реализовывать ветвление if. Кажется, это будет добавлять дополнительную нагрузку на решатель


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 12, 2017, 12:24
Какое ветвление?


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 12:26
Код
C++ (Qt)
if (current / total != progressValue){
   emit ...
}


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 12, 2017, 12:31
Да это ерунда вычислить. А код можно обобщить в классе

Код
C++ (Qt)
class Progress
{
public:
 Progress (quint64 total);
 void setValue (quint64 value);
 
signals:
 void progress (int);
};
 


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 13:26
А сам прогресс где создаёшь при этом и как с ним взаимодействуют морда и солвер?
У меня, кстати, солвер несколько последовательных операций имеет, прогресс которых хотелось бы отображать.


Название: Re: Новичкам про потоки
Отправлено: Пантер от Январь 12, 2017, 13:29
Тут как захочешь, так и делай, вариантов вагон и пару тележек.


Название: Re: Новичкам про потоки
Отправлено: Igors от Январь 12, 2017, 16:00
Хотел бы ещё обсудить такую вещь. При использовании потоков как антифриз для морды, каким образом лучше реализовывать отображение прогресса?
Сейчас я использую примерно следующее: передаю указатель на структуру, где хранится наименование операции, текущий процент выполнения. Диалог прогресса с частотой 100 мс опрашивает эту структуру, а решатель ведёт в неё запись. Интуитивно чувствую, что не самый лучший вариант. Есть предложения?
По таймеру с какой-то частотой нормально, только счетчик(и) обновляемые рабочими нитками делать атомарными (напр QAtomicInt)


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 16:03
Не, в моём случае не страшно их делать не атомарными. Запись делает 1 поток, чтение другой. Не важно, если на момент отображения реальный прогресс убежит вперёд.


Название: Re: Новичкам про потоки
Отправлено: Авварон от Январь 12, 2017, 16:05
Concurrent умеет о прогрессе уведомлять через QFutureWatcher


Название: Re: Новичкам про потоки
Отправлено: m_ax от Январь 12, 2017, 16:08
Не, в моём случае не страшно их делать не атомарными. Запись делает 1 поток, чтение другой. Не важно, если на момент отображения реальный прогресс убежит вперёд.
Там atomic нужен чтоб значение счётчика не похерилось, в ситуации одновременного чтения и записи)


Название: Re: Новичкам про потоки
Отправлено: Igors от Январь 12, 2017, 17:00
Не, в моём случае не страшно их делать не атомарными. Запись делает 1 поток, чтение другой. Не важно, если на момент отображения реальный прогресс убежит вперёд.
Ну если там аж одна нитка считает и UI свободно - то как угодно, не грех и на сигнал потратиться. Проблемы с индикатором возникают когда задача/кластер соразмерим с обновлением или когда главная тоже считает. 


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 17:24
m_ax, что значит похерилось, не совсем понимаю, оно может как-то испортиться? Если я в ячейку, где хранится 5 пишу 6 и одновременно считываю эту же ячейку, то при считывании получу или 5 или 6, в моём понимании.


Название: Re: Новичкам про потоки
Отправлено: m_ax от Январь 12, 2017, 17:28
m_ax, что значит похерилось, не совсем понимаю, оно может как-то испортиться? Если я в ячейку, где хранится 5 пишу 6 и одновременно считываю эту же ячейку, то при считывании получу или 5 или 6, в моём понимании.
Если, в общем случае, запись/чтение не атомарна, то при одновременной записи и чтении, кто его знает, что на выходе можно получить..) Может 5, а может 6, а может и 100500  :)  
Короче, лучше не рисковать)


Название: Re: Новичкам про потоки
Отправлено: Авварон от Январь 12, 2017, 17:46
Если, в общем случае, запись/чтение не атомарна, то при одновременной записи и чтении, кто его знает, что на выходе можно получить..) Может 5, а может 6, а может и 100500  :)  
Короче, лучше не рисковать)

Запись примитивных типов атомарна, то есть записывая 5, 100500 получить никак нельзя.
Другое дело, что реально оно может быть записано не в память, а в кэш ядра, то есть разные потоки будут видеть разное значение (записавший - новое, остальные - старое).
Соответственно, помимо того, что неизвестно, в каком порядке потоки записали 5 и 6, также неизвестно, в каком порядке будут сброшены кэши (и будут ли). Но вариантов не так много - либо старое значение, либо 5 либо 6.


Название: Re: Новичкам про потоки
Отправлено: __Heaven__ от Январь 12, 2017, 17:49
Бугагашеньки, как оно бывает. Но мне везло.


Название: Re: Новичкам про потоки
Отправлено: Igors от Январь 12, 2017, 18:08
Атомарность - дело хорошее. Не раз видел статьи где утверждают, мол, "если действие выполняется одной машинной командой - оно атомарно". Ладно, вот есть команда
Код
C++ (Qt)
inc dword ptr [1234] ;
Но хорошо известно что инкремент не атомарен, более того если откроем реализацию конкретного atomic, то никакого inc там не увидим. Как же так?


Название: Re: Новичкам про потоки
Отправлено: Авварон от Январь 12, 2017, 18:17
Очевидно, что операции связанные с памятью не могут быть "атомарны" (см выше).
Но тут надо определить что значит "атомарность".
В конечном итоге, в памяти может быть что угодно, но это не означает, что где-то мы записали половину числа, а половину "не успели". Нет, мы всегда запишем что-то когда-то целиком. Будет ли это то, что ожидалось? Не факт.


Название: Re: Новичкам про потоки
Отправлено: Igors от Январь 13, 2017, 15:30
Запись примитивных типов атомарна, то есть записывая 5, 100500 получить никак нельзя.
В общем случае нет, пример
Код
C++ (Qt)
#pragma pack(push, 1)
struct CData {
char type;
double value;   // qint64
};
#pragma pack(pop)
 
Вот и "бугага" и все такое. ВСЕГДА используйте штатный atomic (из std, Qt, tbb или откуда хотите). Даже для int'ов которые уж точно сами по себе атомарны.

Очевидно, что операции связанные с памятью не могут быть "атомарны" (см выше).
Но тут надо определить что значит "атомарность".
В конечном итоге, в памяти может быть что угодно, но это не означает, что где-то мы записали половину числа, а половину "не успели". Нет, мы всегда запишем что-то когда-то целиком. Будет ли это то, что ожидалось? Не факт.
Здесь дело не в этом. Возможно машинная команда inc и атомарна, но мы никак не сможем вернуть "верное" значение которое должен возвращать оператор ++. Как все знают, здесь "верное" не значит "текущее"





Название: Re: Новичкам про потоки
Отправлено: Vlad_QtCr от Май 22, 2018, 11:05
Пришел новичок ознакомиться с тредом "Новичкам про потоки". Тему не раскрыли((
Прочитал несколько статей с примерами по многопоточности, но не до конца ясно как оно работает и как можно использовать в своих целях (свои примеры с нуля не получаются). Был бы рад готовому примеру (задание-просьба в конце поста).
Считается, что если форма при вычислениях "замирает" (циклы, ожидание ответа устройств...), то лучшим решением будет вставка строки qApp->processEvents(0); вместо создания потоков.
На мой взгляд потоки интересны в приложениях, где дается команда, например, на скачивание файла после чего работа должна продолжаться (т.е. скачивание отправляют в поток). В однопроцессорной системе многопоточное приложение сильно нагружает машину. Фактически выигрыша никакого, т.к. есть определенное количество команд, которые необходимо выполнить и если есть второй процессор (ядро), то он возьмет часть команд на себя. Если ядер нет, то каждый поток будет перываться для того, чтобы процессор выполнил кусочек кода другого потока (это я для таких же новичков как я, которые пока не получили здесь ответа). Наверное перед запуском лучше проверить возможности процессора и тогда принять решение о включении потоков. Не буду дальше философствовать, приведу ожидаемый пример:
Предположим, на диске есть фотка (сделаем кнопку обзора и открытия файла). Пользователь выбирает фоту и она открывается, но! Считывание ведется побайтно. Объем файла делится надвое. Первый поток (не уверен, но видимо основная часть программы) читает файл от 1 байта до (Length_of_file/2), вторая от (Length_of_file/2)+1 до конца файла. На форме GraphicsView или что-то подобное, куда выводятся точки. Очень желательно, чтобы весь проект был создан в Qt Creator.
Почему такой странный пример?
Во множестве руководств можно прочитать про многопоточность, но на представленых примерах не чувствуется фишки многопоточности. Автор одного примера (Шлее?) выводит  на экран букву "А", а во втором потоке "В". Дескать вот, все работает. Моя задача реально покажет возможности multithreading. Кроме того решается вопрос чтения открытого файла из каждого потока. Понятно, что задача, которая справилась быстрее должна "отрубить" себя, уступив ресурсы соседу (можно наверное просто делать return;, хотя лучше дождусь вашего ответа).
Такой вот пример, может ли кто написать? Код должен быть простым и несложным насколько это возможно. Цепляю образец (Qt 4.8 Creator 4.5)