Многопоточная производительность 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
?
Ответы
Ответ 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 (конструкция нижнего уровня), вероятно, лучше подходит для операции (каждый из них представляет один поток).