Russian Qt Forum
Ноябрь 25, 2024, 22:05 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Как организовать многопоточность? Выбор модели  (Прочитано 21668 раз)
rudireg
Гость
« : Октябрь 22, 2016, 19:10 »

Привет. Я новичек в Qt  и хотелось бы получить совет.

Задача написать многопоточное приложение.
Приложение для работы с социальной сетью инстаграм.
Например загрузили в программу 200 аккаунтов, дали задачу этим аккаунтам, и далее каждый аккаунт выполняет свою задачу в своем отдельном потоке.

Я вижу три типа потоков в программе:
1) Есть главный поток UI  (он отвечает за работу с интерфейсом)
2) Главный UI поток создает дочерний поток, назовем его MASTER (он служит для создания дочерних потоков и управления мим , которые уже ведут работу с аккаунтами)
    MASTER  выдает дочерним потокам задачи и принимает от них сигналы, а так же отвечает за вывод информации в UI
3) Потоки WORKER - они создаются MASTER-ом и им контролируются.

В итоге получаем картину, UI поток создает MASTER поток, далее MASTER  создает WORKER потоки (их может быть много...)

ВОПРОС: Какую модель реализации многопоточности выбрать?
QtConcurrent - тут не подойдет.
Наследоваться от QThread - нет желания.
 Смотрел в сторону QThreadPool - но не знаю подойдет ли оно... ибо если аккаунтов 100, то повзволит ли он пустить 100 потоков.
изначально хотел просто создать в MASTER потоке локальную переменную QList<QThread*> stack; для хранения потоков....

Тут нюанс вот в чем, аккаунтов много, каждый аккаунт в своем потоке... НО.... есть нюанс... потоки не работают на износ, он сделал свою задачу и должен заснуть на минуту и снова сделать свою задачу, и все это дело зациклить... покуда либо не завершится задача, либо поток MASTER  его не остановит принудительно

Как бы вы организовали модель многопоточности?
« Последнее редактирование: Октябрь 22, 2016, 19:15 от rudireg » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #1 : Октябрь 22, 2016, 19:29 »

Нет никакой необходимости заводить 100 потоков - это разбазаривание ресурсов процессора.
Достаточно пула потоков (количество воркеров в нем можно подбирать в зависимости от, например, количества обслуживаемых аккаунтов) и очереди заданий. Задания в очередь может помещать хоть GUI-поток, хоть MASTER-поток. Воркеры достают задания из очереди и выполняют.
Для этого подойдет и QThread, и QtConcurrent с QThreadPool.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #2 : Октябрь 22, 2016, 20:01 »

А если все задачи сводятся к отправке сетевых запросов и получению ответов, то можно вообще без потоков обойтись. Весь сетевой обмен в Qt может работать асинхронно.
Записан
Bepec
Гость
« Ответ #3 : Октябрь 22, 2016, 20:03 »

Пользователю можно написать, что там 200 потоков, но спокойно работать будут и два. Один на приём, второй на обработку.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #4 : Октябрь 22, 2016, 20:05 »

Пользователю можно написать, что там 200 потоков
Зачем?  Строит глазки
Записан
rudireg
Гость
« Ответ #5 : Октябрь 22, 2016, 20:27 »

Спасибо. Почитаю про QThreadPool, попробую его в реализации...
Буду набираться опыта.

Конечно... сетевые запросы отправляются... и принимаются. Так же полученные пакеты обрабатываются для получения данных...
плюс идет обработка этих данных в SQLite базе... (Наверное каждый поток будет отдельно обращаться к базе через Мютексы)
Каждый аккаунт должен быть отображен в UI - (а именно в QTableWidget)  с информацией о ходе выполнения операции...
в любой момент поток по желанию пользователя может  останавливаться и запускаться снова...
И разнообразие Задач выполняющиеся в WORKER может быть большое множество - их указывает пользователь перед стартом потока WORKER

Например вы писали, что поток Worker берет задание из стека заданий и выполняет его.
А если допустим стоит задание лайкать фотографии по списку пользователей.... с паузой в 50 секунд... (то есть каждые 50 секунд отправляется сетевой запрос) то как бы вы организовали стек задач?
Например: Каждый Worker имеет свой стек задач (хотя можно сделать общий для всех Workerов), и каждые 50 секунд MASTER отправляет в стек  новое задание ( например лайкнуть)...
Или же лучше сразу передать весь список пользователей (тех кого нужно лайкнуть) и указать задержку 50 секунд...
Я к тому, что если я передам WORKERу  сразу всех пользователей (например 1000) и задам паузу (50 секунд), то выполнение задачи в потоке будет выполнятся продолжительное время...
тоесть там будет sleep(50 секунд)  -  не повлияет ли это на загруженность работы QThreadPool?
Не будет ли QThreadPool думать что поток еще выполняет свое задание даже если там пауза... и потому другие потоки будут ожидать его выполнения... хотя он просто стоит на паузе
« Последнее редактирование: Октябрь 22, 2016, 20:29 от rudireg » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #6 : Октябрь 22, 2016, 20:46 »

Например вы писали, что поток Worker берет задание из стека заданий и выполняет его.
А если допустим стоит задание лайкать фотографии по списку пользователей.... с паузой в 50 секунд... (то есть каждые 50 секунд отправляется сетевой запрос) то как бы вы организовали стек задач?
IMHO, тут архитектурно нужно разделить понятия задачи и операции. Задача это набор операций, выполняемых по заданному алгоритму. Задача хранит список пользователей и другие необходимые параметры, и по таймеру в 50 сек ставит в очередь операцию - лайкнуть фотографию.
Вот для операций, как раз подойдет пул потоков. Все задачи ставят в его очередь необходимые для выполнения операции, а воркеры пула по мере возможности их выполняют.
Поищите, на сайте были темы про очереди заданий с примерами кода.

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Октябрь 23, 2016, 08:23 »

.. покуда либо не завершится задача, либо поток MASTER  его не остановит принудительно
Нет такой легкой/удобной возможности "остановить принудительно"

Как бы вы организовали модель многопоточности?
Она давно известна. Запускается столько ниток сколько ядер и им скармливаются задачи. Нужно стремиться к тому чтобы любая задача могла быть отдана любой нитке на выполнение. Многочисленные попытки решить проблемы созданием бОльшего числа ниток (обычно закрепляя за ниткой какую-то конкретную задачу - так кажется удобнее) никогда ничего хорошего не давали.

Цитировать
А если вот длинная задача - ну не могу же я ждать ее завершения, если у меня появилась др задача которую надо сделать как можно быстрее
Значит надо нарезать длинную на более мелкие, в этом обычно работа и состоит (да, это часто непросто)
Записан
rudireg
Гость
« Ответ #8 : Октябрь 23, 2016, 13:36 »

Интересный принцип работы QTreadPool.  Веселый

Псевдокод
Код
C++ (Qt)
Worker* worker = new Worker;  // Inherit QRunnable
 
QThreadPool *threadPool = new QThreadPool;
 
for(int i=0; i < 20; i++) {
       if(!threadPool->globalInstance()->tryStart(worker))
           qDebug() << i << " tryStart = false";
       else
           qDebug() << i << " tryStart = true";
       };
 

Так можно проверить какое количество ниток QThreaPool резервирует для работы...
В моем случае  QThreaPool зарезервировало 8 ниток.
У меня процессор имеет 4 ядра (плюс каждое ядро разделено на 2 виртуальных) итого 8 ядер.
« Последнее редактирование: Октябрь 23, 2016, 13:38 от rudireg » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #9 : Октябрь 23, 2016, 13:45 »

int   QThreadPool::activeThreadCount() const

int   QThreadPool::maxThreadCount() const
void   QThreadPool::setMaxThreadCount(int maxThreadCount)

int QThread::idealThreadCount()
Записан
rudireg
Гость
« Ответ #10 : Октябрь 23, 2016, 15:04 »

Решил попробывать получить количество активных ниток в пуле
int   QThreadPool::activeThreadCount() const

Запускаю проверку активных потоков до и после резервирования ниток
Код
C++ (Qt)
Worker* worker = new Worker;  // Inherit QRunnable
QThreadPool *threadPool = new QThreadPool;
 
qDebug() << "activeThreadCount: " << threadPool->activeThreadCount();
 
for(int i=0; i < 20; i++)
       threadPool->globalInstance()->tryStart(worker);
 
qDebug() << "activeThreadCount: " << threadPool->activeThreadCount();
 

В итоге  и ДО и ПОСЛЕ получаю значение 0

в Worker выполняется этот код
Код
C++ (Qt)
void Worker::run()
{
       QThread::sleep(10); // Pause 10 sec
}
 

Почему получаю 0 ? Ведь там есть потоки в работе...
« Последнее редактирование: Октябрь 23, 2016, 15:14 от rudireg » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #11 : Октябрь 23, 2016, 15:56 »

activeThreadCount показывает количество резервированных потоков.
Записан
rudireg
Гость
« Ответ #12 : Октябрь 23, 2016, 17:33 »

activeThreadCount показывает количество резервированных потоков.
Да, действительно, следует зарезервировать потоки, лишь после этого мы увидим кол. зарезервированных потоков.
Код
C++ (Qt)
QThreadPool* threadPool = new QThreadPool;
 
threadPool->reserveThread();
threadPool->reserveThread();
threadPool->reserveThread();
 
qDebug() << "Active Threads: " << threadPool->activeThreadCount(); //Will show active Threads
 
 
Записан
rudireg
Гость
« Ответ #13 : Октябрь 24, 2016, 09:28 »

Выскажите ваше мнение... по поводу того, что в интернете очень часто можно встретить программы, в которых можно указать кол. потоков для работы.
Например вот


На скриншоте интерфейса программы указано кол. потоков 50 штук.
Я так понимаю что ОС не запускает на самом деле 50 потоков, а создает псевдо потоки.... (видимо они стоят в очереди на выполнение)
Допустим аппаратная мощность компьютера ограничивается 2 ядрами.
Выходит что на самом деле используется 2 потока а не 50 в итоге...

Я думаю указание 50 потоков в подобных программах скорее для упрощения реализации логики работы программы, обмена данными между псевдо-нитками и главным потоком
ведь тогда ненужно делать допустим очереди заданий, каждому псевдо-потоку соответсвено назначается свое индивидуальное задание.
Непонимающий На ваш взгляд какие есть минусы в таком подходе с точки зрения аппаратного использования вычислительных мощностей компьютера?
Почему именно этот метод получил такое широкое распространение в народе...
« Последнее редактирование: Октябрь 24, 2016, 09:40 от rudireg » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #14 : Октябрь 24, 2016, 09:38 »

Выходит что на самом деле используется 2 потока а не 50 в итоге...
Да можно запустить и 100 потоков, только проку от них никакого не будет.
А разработчику, как вы заметили, так было проще реализовать.
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.35 секунд. Запросов: 23.