Russian Qt Forum

Qt => Кладовая готовых решений => Тема начата: Igors от Февраль 04, 2016, 09:01



Название: ThreadTeam
Отправлено: Igors от Февраль 04, 2016, 09:01
Добрый день

Почти каждая тема "многопоточности" сваливается в проблемы синхронизации и/или классы QtConcurrent. Вот стало интересно - а можно как-то без всего этого, чисто на слот/сигнал?

У нас есть N задач которые мы хотим скормить какому-то числу ниток (обычно = числу ядер). При этом обычный ф-ционал: остановить, продолжить, отменить. Принципиально не используем никаких примитивов синхронизации, даже любимых мною атомиков. И делаем "по-богатому": задача стартовала/закончилась- сигнал в UI, все отмены/продолжения - тоже испускаем сигналы. Конечно так будут накладные расходы, но мы считаем задачи у нас достаточно жирные (хорошие доли секунды и больше). В общем, за скоростью не гонимся

Чтобы юзать надо создать нужные слоты. Пример
Код
C++ (Qt)
// создаем "бригаду"
CControl * ctl = new CControl(QThread::idealThreadCount()); .
 
// слот который считает,  (только он выполняется в рабочих нитках, все остальные в главной)
ctl->Connect2Calc(this, &CTestWin::SlotCalc);
 
// слот скармливающий очередную задачу
QObject::connect(ctl, &CControl::SignalTaskGet, this, &CTestWin::SlotGetTask);
 
// слот "задача началась"
QObject::connect(сtl, &CControl::SignalTaskBegin, this, &CTestWin::SlotTaskBegin);
 
// слот "задача сделана"
QObject::connect(ctl, &CControl::SignalTaskDone, this, &CTestWin::SlotTaskDone);
 
// слот "нитка не имеет больше задач"
QObject::connect(ctl, &CControl::SignalNoTask, this, &CTestWin::SlotNoTask);
 
// отслеживает останов, пуск, отмена и готово
QObject::connect(ctl, &CControl::SignalStateChanged, this, &CTestWin::SlotStateChanged);
 
// погнали считать
ctl->Go();
 
Окончание ловим в SlotStateChanged. В аттаче пример с UI (pro файл имеется)

Это все, благодарю за внимание


Название: Re: ThreadTeam
Отправлено: poru от Февраль 04, 2016, 15:43
Мне кажется все перемудрено. Давайте уточним. Есть 100 неких вычислений. Есть 4 потока. Запускаем потоки одновременно. В каждом потоке решается только одно вычисление. Как только оно закончилось то в этот поток закидываем следующее вычисление. Правильно? Тогда у меня вопрос - само вычисление есть самостоятельный объект с известным методом или как некоторая функция произвольного класса?


Название: Re: ThreadTeam
Отправлено: Igors от Февраль 04, 2016, 16:04
Давайте уточним. Есть 100 неких вычислений. Есть 4 потока. Запускаем потоки одновременно. В каждом потоке решается только одно вычисление. Как только оно закончилось то в этот поток закидываем следующее вычисление. Правильно?
Да, все верно

Тогда у меня вопрос - само вычисление есть самостоятельный объект с известным методом или как некоторая функция произвольного класса?
Код вычисления - любой слот любого класса, задается в Connect2Calc. Этот слот получает на вход аргумент void * data, это данные для обсчета. Да, я знаю, есть QRunnable, но не лежит к нему душа (жабой отдает). В конце-концов, данные могут быть и POD структурой


Название: Re: ThreadTeam
Отправлено: poru от Февраль 05, 2016, 15:01
Вот какие у меня замечания. Как нам известно "сигнал" предназначен, что бы сообщать об произошедших изменениях, а "слот" есть реакция на эти изменения. В Вашем случае сигналы SignalTaskGet, SignalTaskCalc не сообщают об изменения, а вызывают конкретные действия. Это не соответствует идеологии сигналов/слотов. И от этого такая запутанность.

И для чего таскать по всем вызовам ctl и thread?


Название: Re: ThreadTeam
Отправлено: Igors от Февраль 05, 2016, 16:48
Вот какие у меня замечания. Как нам известно "сигнал" предназначен, что бы сообщать об произошедших изменениях, а "слот" есть реакция на эти изменения. В Вашем случае сигналы SignalTaskGet, SignalTaskCalc не сообщают об изменения, а вызывают конкретные действия. Это не соответствует идеологии сигналов/слотов. И от этого такая запутанность.
Возможно это впечатление оттого что CThread публичный класс и есть возможность его наследования (хотя нужно это редко). Поэтому чтение хедера начинается "с самых потрохов"  :)

И для чего таскать по всем вызовам ctl и thread?
ctl - на мой вкус опрятнее чем приводить sender'a. А thread может оказаться необходимым. Напр известно что неск задач должны выполняться только последовательно - ну мы их в одну нитку и закинем. Или вообще, какой-то нитке не будем давать никаких задач (пока).

Др интересные возможности: задача может порождать др задачи, вообще число задач заранее неизвестно. Или задача требует чтобы на момент ее старта др задачи были уже выполнены. И.т.п. Такие вещи здесь делаются без труда.