Подтвердить что ты не робот

Многопоточная производительность QtConcurrent Vs QThread со многими потоками

Предположим, что вашему приложению необходимо запустить функцию в нескольких потоках, число которых больше, чем количество ядер/потоков ЦП. Один из способов - использовать QtConcurrent и установить максимальное количество потоков:

MyClass *obj = new MyClass;

QThreadPool::globalInstance()->setMaxThreadCount(30);

for(int i=0;i<30;i++)
    QtConcurrent::run(obj, &MyClass::someFunction);

Другой способ состоит в том, чтобы иметь несколько объектов и переместить их в разные потоки с помощью moveToThread:

for(int i=0;i<30;i++)
{
        MyClass *obj = new MyClass;
        QThread *th = new QThread();
        obj->moveToThread(th);
        connect(th, SIGNAL(started()), obj, SLOT(someFunction()) );
        connect(obj, SIGNAL(workFinished()), th, SLOT(quit()) );
        connect(th, SIGNAL(finished()), obj, SLOT(deleteLater()) );
        connect(th, SIGNAL(finished()), th, SLOT(deleteLater()) );

        th->start();
}

Поскольку количество потоков больше, чем количество ядер ЦП, при запуске потоки должны переключаться между разными ядрами.

Вопрос в том, имеют ли два подхода разные действия или нет? то есть переключение a QThread отличается от того, которое выполняется с помощью QtConcurrent::run?

4b9b3361

Ответ 1

Я согласен с первым ответом, но хочу что-то добавить.

QThread - это класс низкого уровня, который запускает только функции, специфичные для ОС. Что такое QtConcurrent? Ответ находится в Qt исходном коде.

Первый уровень: run

QFuture<T> run(T (*functionPointer)())  
{
        return (new StoredFunctorCall0<T, T (*)()>(functionPointer))->start();
}

Вторые:

struct StoredFunctorCall0: public RunFunctionTask<T>    { ...

Третий:

template <typename T>
class RunFunctionTaskBase : public QFutureInterface<T> , public QRunnable
{ ...

Теперь о QRunnable. Когда мы начинаем QRunnable с QThreadPool, мы делаем:

start(), который вызывает tryStart(), который вызывает startThread(), которые работают с QThreadPoolThread (и это Подкласс QThread), и он, наконец, вызывает start() QThread.

И, конечно, эта цепь не полная, длинная дорога, не так ли? Так как я знаю, когда мы используем абстракцию, у нас есть штраф абстракции (QtConcurrent имеет более высокий штраф, чем QThread), но конечный результат тот же, это QThread.

Ответ 2

Короткий ответ: это зависит от характера/логики рабочей нагрузки.

QtConcurrent запускает пул потоков, и это API более высокого уровня не подходит для запуска большого количества операций блокировки: если вы выполняете много операций блокировки, вы в конечном итоге исчерпаете пул и добавите другие запросы в очередь. В этом случае QThread (конструкция нижнего уровня), вероятно, лучше подходит для операции (каждый из них представляет один поток).