Russian Qt Forum

Qt => Вопросы новичков => Тема начата: opera от Октябрь 05, 2020, 19:43



Название: многопоточность, func arg
Отправлено: opera от Октябрь 05, 2020, 19:43
Добрый вечер, подскажите пожалуйста, у меня есть класс MyClass с функцией, которая должна осуществлять некоторые рассчеты:
Код:
void MyClass::calculationFunc(const QVector& list)
{
   for(int i = 0; i < list.size(); i++)
  {
          my work
  }
}
как мне вызывать функцию одного объекта MyClass obj в нескольких потоках, что-то типо:
Код:
for(int n = 0;  n < 3; n++)
{
     QThread* thread = new QThread(this);
     ......здесь не знаю что писать
}
то есть создавалось бы три потока, каждый из которых использовал объект MyClass , а именно функцию calculationFunc с переданными в нее аргументами, подскажите пожалуйста пример кода, спасибо!


Название: Re: многопоточность, func arg
Отправлено: tux от Октябрь 05, 2020, 20:19
https://code.qt.io/cgit/qt/qtbase.git/tree/examples/qtconcurrent/runfunction/main.cpp?h=5.15


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 05, 2020, 20:30
https://code.qt.io/cgit/qt/qtbase.git/tree/examples/qtconcurrent/runfunction/main.cpp?h=5.15


а именно с классами , функциями и объектами?Потому что в объекте много переменных и других объектов, которые как раз и используются в рассчете функции


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 06, 2020, 10:17
Нашел вот такой способ:

Код:
class MyClass
{
public:
    MyClass()
    {

    }
    void hello(QString name, long long value)
    {
        int xx =_intValue;
        long long x = value;//2111111111;
        while(x)
            x--;
        qDebug() << "Hello" << name << "from" << QThread::currentThread();
    }
private:
    int _intValue = 4;
};

int main(int argc, char *argv[])
{

    QApplication a(argc, argv);
    MyClass class1;
    QFuture<void> f1 = QtConcurrent::run(&class1,&MyClass::hello, QString("Alice"), 2111111111);
    QFuture<void> f2 = QtConcurrent::run(&class1,&MyClass::hello, QString("Bob"), 3111111111);
    f1.waitForFinished();
    f2.waitForFinished();
return 0;
}


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 06, 2020, 12:24
да, вам это и советовали. функцию лучше сделать константной, чтобы бить себе по рукам за попытку поменять данные класса из разных потоков (это можно, но нужен будет мьютекс)


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 06, 2020, 13:47
да, вам это и советовали. функцию лучше сделать константной, чтобы бить себе по рукам за попытку поменять данные класса из разных потоков (это можно, но нужен будет мьютекс)
спасибо, хороший совет)


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 06, 2020, 15:00
а как сделать, если я не знаю количества функций(зависит от количества ядер),но хочу использовать ожидание waitForFinished:
Код:
int count_thread = QThread::idealThreadCount()-1;
    QFuture <void> f;
    for(int i = 0; i < count_thread; i++)
    {
        f = QtConcurrent::run(_managerObj,&iManager::calcRebate, i);
    }
    f.waitForFinished();
использование вектора не удается


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 06, 2020, 16:16
Код:
int count_thread = QThread::idealThreadCount()-1;
    std::vector<QFuture<void>> futures;
    futures.reserve(count_thread);
    for(int i = 0; i < count_thread; i++)
    {
        futures.push_back(QtConcurrent::run(_managerObj,&iManager::calcRebate, i));
    }
    for (auto &f: futures) // const auto?
         f.waitForFinished();


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 06, 2020, 17:10
Код:
int count_thread = QThread::idealThreadCount()-1;
    std::vector<QFuture<void>> futures;
    futures.reserve(count_thread);
    for(int i = 0; i < count_thread; i++)
    {
        futures.push_back(QtConcurrent::run(_managerObj,&iManager::calcRebate, i));
    }
    for (auto &f: futures) // const auto?
         f.waitForFinished();
спасибо!


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 08, 2020, 13:52
А можно ли было это же записать через map?Если да, то можно тоже кодовым примером?


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 08, 2020, 14:36
А что ключом-то должно быть?


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 08, 2020, 14:40
А что ключом-то должно быть?
QtConcurrent::map() применяет функцию к каждому элементу в контейнере, изменяя элементы на месте - это из документации
соответственно контейнером должен быть QVector<int> listI


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 08, 2020, 14:47
А, этот map =) я подумал об std::map.

типа такого? код не компилировал=)
Код:
int count_thread = QThread::idealThreadCount()-1;
    std::vector<i> tasks;
    tasks.reserve(count_thread);
    for(int i = 0; i < count_thread; i++)
    {
        tasks.push_back(i);
    }
    const auto future = QtConcurrent::map([&_managerObj](int i){ _managerObj->calcRebate(i); return i; }, tasks);
    future.waitForFinished();


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 08, 2020, 14:58
А, этот map =) я подумал об std::map.

типа такого? код не компилировал=)
Код:
int count_thread = QThread::idealThreadCount()-1;
    std::vector<i> tasks;
    tasks.reserve(count_thread);
    for(int i = 0; i < count_thread; i++)
    {
        tasks.push_back(i);
    }
    const auto future = QtConcurrent::map([&_managerObj](int i){ _managerObj->calcRebate(i); return i; }, tasks);
    future.waitForFinished();
да, что-то типо того, только хотелось бы разъяснение,что на каком месте идет, вижу лямбду

попытался передать два параметра(два списка), но не компилится:


Код:
QVector<QString> listStr;
    QVector<long long> listLong;
    auto future = QtConcurrent::map([&classObj](QString str,long long l){ classObj.incrCalc(str,l); return str; }, listStr,listLong);


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 08, 2020, 15:05
Если вы передаете параметром vector<T> то функция маппера должна иметь вид T foo(const T&) - то есть берет старое значение и возвращает новое. Поэтому нужна лямбда чтобы сигнатура была "правильной". Возвращать можно то, что передали, или 0, не особо важно.
Два списка передать нельзя, надо их склеить в один список, например пар или структур.
отличие map от mapped что первый модифицирует контейнер inplace (перeписывая значения новым, "смапленным" значением) когда вторая - "возвращает" значения через список в самой QFuture


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 08, 2020, 15:47
"Пространство имен QtConcurrent предоставляет высокоуровневые API, которые делают возможным написание многопоточных программ без использования низкоуровневых потоковых примитивов, таких как мьютексы, блокировки чтение-запись, условия ожидания или семафоры." -это значит, что я не могу использовать мьютексты и все остальное для синхронизации?


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 08, 2020, 17:08
можете, но это убивает основную идею QtConcurrent - не использовать их=) QtConcurrent использует разделение по данным - если к одной ячейке массива имеет доступ один поток, ничего синхронизировать не надо. если у вас возникает шаред стейт, то неясно, зачем нужен конкаррент


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 08, 2020, 19:09
можете, но это убивает основную идею QtConcurrent - не использовать их=) QtConcurrent использует разделение по данным - если к одной ячейке массива имеет доступ один поток, ничего синхронизировать не надо. если у вас возникает шаред стейт, то неясно, зачем нужен конкаррент
в моем случае я просто хотел с самого начала запускать в разных потоках одну и ту же функцию одного и того же объекта с разными входными данными.Тогда возвращусь к нему, как это через обычный QThread реализовать?
Если же я делаю moveToThread то я работаю с объектом в потоке, куда поместил объект, а если я хочу запустить одну и туже функцию в разных ппотоках, только через конкурент?


Название: Re: многопоточность, func arg
Отправлено: Igors от Октябрь 09, 2020, 07:31
можете, но это убивает основную идею QtConcurrent - не использовать их=) QtConcurrent использует разделение по данным
Ну не то чтоб совсем уж "убивает"  :), но "противоречит" или "не согласуется". И не "использует", а "предполагает" (разделение по данным)

в моем случае я просто хотел с самого начала запускать в разных потоках одну и ту же функцию одного и того же объекта с разными входными данными.Тогда возвращусь к нему, как это через обычный QThread реализовать?
Если же я делаю moveToThread то я работаю с объектом в потоке, куда поместил объект, а если я хочу запустить одну и туже функцию в разных ппотоках, только через конкурент?
"Поток" - это просто "исполнитель", ему совершенно все равно какую ф-цию какого объекта исполнять. Разница лишь в том откуда (или как) берется эта нитка-исполнитель: автоматом из пула (QtConcurrent) или создается руками (QThread)

И в любом случае если "одного и того же объекта" то Ваша забота чтобы 2 или более потоков не писали одновременно одни и те же данные


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 09, 2020, 09:59
Поток" - это просто "исполнитель", ему совершенно все равно какую ф-цию какого объекта исполнять. Разница лишь в том откуда (или как) берется эта нитка-исполнитель: автоматом из пула (QtConcurrent) или создается руками (QThread)
И в любом случае если "одного и того же объекта" то Ваша забота чтобы 2 или более потоков не писали одновременно одни и те же данные
да, но я же не могу поместить в два разных потока один и тот же объект , мне как минимум его плодить(копировать) прийдется или я что-то не понимаю, поэтому и спрашиваю, как запустить функцию func(int i) объекта obj в разных потоках, но от одного объекта?


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 09, 2020, 12:05
да, но я же не могу поместить в два разных потока один и тот же объект , мне как минимум его плодить(копировать) прийдется или я что-то не понимаю, поэтому и спрашиваю, как запустить функцию func(int i) объекта obj в разных потоках, но от одного объекта?

Можете через run, с синхронизацией.
run это достаточно низкоуровневый метод, просто удобная обертка над QRunnable - какие задачи ставить, вам решать.
Я имел ввиду map/reduce - map тем и хорош что не требует синхронизаций, а значит позволяет параллельно обрабатывать огромные массивы данных.
Но вообще в многопоточном программмировании лучше избегать shared state если он не константный - там где блокировка, там обычно узкое место. Поэтому зачастую скопировать выходит дешевле (как в терминах скорости разработки так и производительности)


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 09, 2020, 13:00
Вы имеете в виду переопределения метода run?run-это же start()?
можно пример, кусочек хотя бы некомпилированный)?


Название: Re: многопоточность, func arg
Отправлено: Авварон от Октябрь 09, 2020, 13:07
QtConcurrent::run


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 09, 2020, 13:09
QtConcurrent::run
а , ну это то я понял, я думал через QThread скажите


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 13, 2020, 15:12
еще вопрос по многопоточности, если я использую конкурент, как мне сделать, чтобы в этот момент gui работал в главном потоке, а конкуренты в других и не влияли на отображение окон при своих вычислениях, а то сейчас с конкурентом окно не в отдельном потоке?


Название: Re: многопоточность, func arg
Отправлено: RedDog от Октябрь 13, 2020, 16:03
GUI из отдельного потока вообще нельзя напрямую дергать.
Окна всегда должны быть в главном потоке, из него можно делать QtConcurrent::run методов, которые напрямую графику не дергают.


Название: Re: многопоточность, func arg
Отправлено: opera от Октябрь 13, 2020, 16:11
GUI из отдельного потока вообще нельзя напрямую дергать.
Окна всегда должны быть в главном потоке, из него можно делать QtConcurrent::run методов, которые напрямую графику не дергают.

У меня вот так написано в main.cpp:
Код:
MainWindow* w = new MainWindow();
w->show();

simpleCalculateClass classObj;

std::vector <QFuture<QString>> listFuncs;
int count_thread = QThread::idealThreadCount()-1;
for(int i = 0; i < count_thread; i++)
    listFuncs.push_back(QtConcurrent::run(&classObj,&simpleCalculateClass::incrCalc, QString::number(i), 1111111111));

и окно не загружается, пока происходит calculate


UPDATE:
просто надо было waitForFinished убрать, понял