首页 > 编程语言 >深入理解Qt多线程编程(QtConcurrent)

深入理解Qt多线程编程(QtConcurrent)

时间:2024-07-02 17:30:47浏览次数:25  
标签:map square Qt int waitForFinished QtConcurrent qDebug 多线程

多线程编程在现代软件开发中变得越来越重要,它能够提高应用程序的响应速度和处理性能。在Qt框架中,除了QThreadPool,QtConcurrent也是一个强大的工具,用于简化和管理多线程编程。

目录

概述

QtConcurrent模块提供了一组便捷的函数,用于在不显式创建和管理线程的情况下实现并发编程。它通过将任务提交给线程池来执行,从而避免了频繁创建和销毁线程带来的性能开销。QtConcurrent非常适合处理需要并行执行的批量任务,并且能够自动管理线程和任务的分配。

接口详解

QtConcurrent::run

QtConcurrent::run用于将一个可调用对象(如函数、Lambda表达式或成员函数)提交给线程池执行。这个函数适用于简单的并行任务。

使用案例

执行简单的任务
void myFunction() {
    qDebug() << "Function is running in thread" << QThread::currentThread();
    QThread::sleep(2);
    qDebug() << "Function completed in thread" << QThread::currentThread();
}

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

    QFuture<void> future = QtConcurrent::run(myFunction);

    future.waitForFinished(); // 等待任务完成

    return app.exec();
}

执行带返回值的任务
int myFunction()
{
    qDebug() << "Function is running in thread" << QThread::currentThreadId();
    QThread::sleep(1);
    qDebug() << "Function completed in thread" << QThread::currentThreadId();
    return 1;
}

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

    QFuture<int> future = QtConcurrent::run(myFunction);

    future.waitForFinished(); // 等待任务完成

    int result = future.result();
    qDebug() << "received value: " << result;

    return a.exec();
}

执行带返回值和参数的任务
int square(int num)
{
    qDebug() << "Function is running in thread" << QThread::currentThreadId();
    return num * num;
}

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

    QFuture<int> future = QtConcurrent::run(square, 2);

    future.waitForFinished(); // 等待任务完成

    int result = future.result();
    qDebug() << "received value: " << result;

    return a.exec();
}

执行Lambda表达式任务
int main(int argc, char* argv[])
{
    QApplication a(argc, argv);

    QFuture<int> future = QtConcurrent::run([]() {
        qDebug() << "Function is running in thread" << QThread::currentThreadId();
        return 2;
    });

    future.waitForFinished(); // 等待任务完成

    int result = future.result();
    qDebug() << "received value: " << result;

    return a.exec();
}

修改引用传递的参数
void square(int& num)
{
    qDebug() << "Function is running in thread" << QThread::currentThreadId();
    num *= num;
}

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

    int num = 2;
    QFuture<void> future = QtConcurrent::run(square, std::ref(num));

    future.waitForFinished(); // 等待任务完成

    qDebug() << "result value: " << num;

    return a.exec();
}

执行可调用对象任务
struct square {
    void operator()(int n) { num = n; }
    int num = 1;
};

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

    square sq;
    qDebug() << "value: " << sq.num; // out: value: 1

    // 这里不是修改sq对象,修改的是sq备份的对象,因为run函数的参数默认是值传递
    QtConcurrent::run(sq, 10).waitForFinished();
    qDebug() << "value: " << sq.num; // out: value: 1

    QtConcurrent::run(std::ref(sq), 20).waitForFinished(); // 通过引用传递才有效
    qDebug() << "value: " << sq.num; // out: value: 20

    return a.exec();
}

源码解析

QtConcurrent::run函数的源码:

template <typename T, typename Param1, typename Arg1>
QFuture<T> run(T (*functionPointer)(Param1), const Arg1 &arg1)
{
    return (new StoredFunctorCall1<T, T (*)(Param1), Arg1>(functionPointer, arg1))->start();
}
... 其他重载不列举了

可以看到run函数是new了一个对象,我们直接看这个对象的源码:

template <typename T, typename FunctionPointer, typename Arg1>
struct StoredFunctorCall1: public RunFunctionTask<T>
{
    inline StoredFunctorCall1(FunctionPointer _function, const Arg1 &_arg1)
      : function(_function), arg1(_arg1) {}
    void runFunctor() override { this->result = function(arg1); }
    FunctionPointer function;
    Arg1 arg1;
};

这段代码需要关注函数runFunctor(),以及基类RunFunctionTask

template <typename T>
class RunFunctionTask : public RunFunctionTaskBase<T>
{
public:
    void run() override
    {
        if (this->isCanceled()) {
            this->reportFinished();
            return;
        }
#ifndef QT_NO_EXCEPTIONS
        try {
#endif
            this->runFunctor();
#ifndef QT_NO_EXCEPTIONS
        } catch (QException &e) {
            QFutureInterface<T>::reportException(e);
        } catch (...) {
            QFutureInterface<T>::reportException(QUnhandledException());
        }
#endif

        this->reportResult(result);
        this->reportFinished();
    }
    T result;
};

这段代码需要关注:

  1. 重载run函数,并在函数内调用了this->runFunctor(),是StoredFunctorCall1类的接口,其实就是调用了QtConcurrent::run传入的可调用对象。

这里重载的是QRunnablerun函数,下面代码可以看出它们的继承关系。

  1. this->reportResult(result);这里把线程函数的返回值记录下来。
  2. 基类RunFunctionTaskBase
template <typename T>
class RunFunctionTaskBase : public QFutureInterface<T> , public QRunnable
{
public:
    QFuture<T> start()
    {
        return start(QThreadPool::globalInstance());
    }

    QFuture<T> start(QThreadPool *pool)
    {
        this->setThreadPool(pool);
        this->setRunnable(this);
        this->reportStarted();
        QFuture<T> theFuture = this->future();
        pool->start(this, /*m_priority*/ 0);
        return theFuture;
    }

    void run() override {}
    virtual void runFunctor() = 0;
};

这里需要关注:

  1. start函数的重载,默认使用的是全局的线程池对象
  2. start函数就是通过线程池来执行任务的
  3. 该类继承了QRunnable
解析总结

QtConcurrent::run()实现原理:创建一个QRunnable子类对象,重载run(),在run()函数内调用线程函数,并将结果通过QFutureInterface记录下来。所有Runnable都是通过QThreadPool来执行,可以用全局的线程池对象,也可以自定义线程池对象。

QtConcurrent::map

QtConcurrent::map函数用于并行地修改序列中的每个元素。它将一个函数应用于容器或迭代器指定范围的每个元素,这个函数会直接修改元素。由于操作是并行执行的,map 函数可以显著加快处理速度,特别是在处理大数据集时。

使用案例

并发修改Qt容器数据
void square(int& n)
{
    n *= n;
}

int main(int argc, char* argv[])
{
    QList<int> listDatas = { 1, 2, 3, 4, 5 };
    QtConcurrent::map(listDatas, square).waitForFinished();
    qDebug() << listDatas;

    QVector<int> vecDatas = { 1, 2, 3, 4, 5 };
    QtConcurrent::map(vecDatas, square).waitForFinished();
    qDebug() << vecDatas;

    QQueue<int> queDatas;
    for (int i = 1; i < 6; ++i)
        queDatas.append(i);
    QtConcurrent::map(queDatas, square).waitForFinished();
    qDebug() << queDatas;

    QMap<int, int> mapDatas = { { 1, 1 }, { 2, 2 }, { 3, 3 } };
    QtConcurrent::map(mapDatas, square).waitForFinished();
    qDebug() << mapDatas;

    QStack<int> stackDatas;
    for (int i = 1; i < 6; ++i)
        stackDatas.append(i);
    QtConcurrent::map(stackDatas, square).waitForFinished();
    qDebug() << stackDatas;

    // error: no match for call to '(QtConcurrent::FunctionWrapper1<void, int&>) (const int&)
    // QSet<int> setDatas = { 1, 2, 3, 4, 5 };
    // QtConcurrent::map(setDatas, square).waitForFinished();
    // qDebug() << setDatas;

    return 0;
}

并发修改标准库容器数据
void square(int& n)
{
    n *= n;
}

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

    std::list<int> listDatas = { 1, 2, 3, 4, 5 };
    QtConcurrent::map(listDatas, square).waitForFinished();
    qDebug() << listDatas;

    std::vector<int> vecDatas = { 1, 2, 3, 4, 5 };
    QtConcurrent::map(vecDatas, square).waitForFinished();
    qDebug() << vecDatas;

    std::deque<int> deqDatas = { 1, 2, 3, 4, 5 };
    QtConcurrent::map(deqDatas, square).waitForFinished();
    for (auto data : deqDatas) {
        qDebug() << data;
    }

    std::array<int, 5> aryDatas = { 1, 2, 3, 4, 5 };
    QtConcurrent::map(aryDatas, square).waitForFinished();
    for (auto data : aryDatas) {
        qDebug() << data;
    }

    // error: no match for call to '(QtConcurrent::FunctionWrapper1<void, int&>) (std::pair<const int, int>&)
    // std::map<int, int> mapDatas = { { 1, 1 }, { 2, 2 }, { 3, 3 } };
    // QtConcurrent::map(mapDatas, square).waitForFinished();
    // qDebug() << mapDatas;

    // error: no match for call to '(QtConcurrent::FunctionWrapper1<void, int&>) (const int&)
    // std::set<int> setDatas = { 1, 2, 3, 4, 5 };
    // QtConcurrent::map(setDatas, square).waitForFinished();
    // for (auto data : setDatas) {
    //     qDebug() << data;
    // }

    return a.exec();
}

注意:
标准库中的std::mapstd::set是不支持的。

QtConcurrent::map为什么能支持QMap,但不支持std::map?
请看后面的源码解析

源码解析

QtConcurrent::map源码:

// map() on sequences
template <typename Sequence, typename MapFunctor>
QFuture<void> map(Sequence &sequence, MapFunctor map)
{
    return startMap(sequence.begin(), sequence.end(), QtPrivate::createFunctionWrapper(map));
}

// map() on iterators
template <typename Iterator, typename MapFunctor>
QFuture<void> map(Iterator begin, Iterator end, MapFunctor map)
{
    return startMap(begin, end, QtPrivate::createFunctionWrapper(map));
}

这里可以看到,底层就是调用了startMap,并且数据都通过收尾迭代器传入。

template <typename Iterator, typename Functor>
inline ThreadEngineStarter<void> startMap(Iterator begin, Iterator end, Functor functor)
{
    return startThreadEngine(new MapKernel<Iterator, Functor>(begin, end, functor));
}

这段代码是调用线程进行处理数据,但在这之前需要把数据和可调用对象functor整合在一个结构中:

// map kernel, works with both parallel-for and parallel-while
template <typename Iterator, typename MapFunctor>
class MapKernel : public IterateKernel<Iterator, void>
{
    MapFunctor map;
public:
    typedef void ReturnType;
    MapKernel(Iterator begin, Iterator end, MapFunctor _map)
        : IterateKernel<Iterator, void>(begin, end), map(_map)
    { }

    bool runIteration(Iterator it, int, void *) override
    {
        map(*it);
        return false;
    }

    bool runIterations(Iterator sequenceBeginIterator, int beginIndex, int endIndex, void *) override
    {
        Iterator it = sequenceBeginIterator;
        std::advance(it, beginIndex);
        for (int i = beginIndex; i < endIndex; ++i) {
            runIteration(it, i, nullptr);
            std::advance(it, 1);
        }

        return false;
    }
};

代码中的map就是可调用对象(即处理数据的线程函数),在runIteration函数内调用了map(*it),所以这里就是将容器元素一个一个传给可调用对象进行处理的,而且都是通过迭代器的解引用来获取数据的。

上面使用案例提到的问题,为什么支持QMap,但不支持std::map?
是因为QMap::iterator指向的是键值对中的value,解引用后得到的是具体的value值;
std::map::iterator指向的是键值对(如std::pair(const int, int)),所以当std::map作为数据传入时会报错:no match for call to '(QtConcurrent::FunctionWrapper1<void, int&>) (std::pair<const int, int>&)'
所以只要容器的迭代器指向的数据类型能和可调用对象的参数类型一致,QtConcurrent::map就能够支持。

QtConcurrent::mapped

QtConcurrent::mappedQtConcurrent::map非常相近,区别就是后者是直接修改原始容器中的数据,而前者不会修改原始容器中的数据,它会创建一个新的结果容器。

使用案例

并发处理Qt容器数据
int square(int n)
{
    n *= n;
    return n;
}

int main(int argc, char* argv[])
{
    QList<int> listDatas = { 1, 2, 3, 4, 5 };
    auto listFuture = QtConcurrent::mapped(listDatas, square);
    listFuture.waitForFinished();
    qDebug() << listDatas << listFuture.results();

    QVector<int> vecDatas = { 1, 2, 3, 4, 5 };
    auto vecFutrure = QtConcurrent::mapped(vecDatas, square);
    vecFutrure.waitForFinished();
    qDebug() << vecDatas << vecFutrure.results();

    QQueue<int> queDatas;
    for (int i = 1; i < 6; ++i)
        queDatas.append(i);
    auto queFuture = QtConcurrent::mapped(queDatas, square);
    queFuture.waitForFinished();
    qDebug() << queDatas << queFuture.results();

    QMap<int, int> mapDatas = { { 1, 1 }, { 5, 5 }, { 3, 3 } };
    auto mapFuture = QtConcurrent::mapped(mapDatas, square);
    mapFuture.waitForFinished();
    qDebug() << mapDatas << mapFuture.results();

    QStack<int> stackDatas;
    for (int i = 1; i < 6; ++i)
        stackDatas.append(i);
    auto stackFuture = QtConcurrent::mapped(stackDatas, square);
    stackFuture.waitForFinished();
    qDebug() << stackDatas << stackFuture.results();

    QSet<int> setDatas = { 1, 2, 3, 4, 5 };
    auto setFuture = QtConcurrent::mapped(setDatas, square);
    setFuture.waitForFinished();
    qDebug() << setDatas << setFuture.results();

    return 0;
}

并发处理标准库容器数据
int square(int n)
{
    n *= n;
    return n;
}

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

    std::list<int> listDatas = { 1, 2, 3, 4, 5 };
    auto listFuture = QtConcurrent::mapped(listDatas, square);
    listFuture.waitForFinished();
    qDebug() << listDatas << listFuture.results();

    std::vector<int> vecDatas = { 1, 2, 3, 4, 5 };
    auto vecFuture = QtConcurrent::mapped(vecDatas, square);
    vecFuture.waitForFinished();
    qDebug() << vecDatas << vecFuture.results();

    std::deque<int> deqDatas = { 1, 2, 3, 4, 5 };
    auto deqFuture = QtConcurrent::mapped(deqDatas, square);
    deqFuture.waitForFinished();
    for (auto data : deqDatas) {
        qDebug() << data;
    }
    qDebug() << deqFuture.results();

    std::array<int, 5> aryDatas = { 1, 2, 3, 4, 5 };
    auto aryFuture = QtConcurrent::mapped(aryDatas, square);
    aryFuture.waitForFinished();
    for (auto data : aryDatas) {
        qDebug() << data;
    }
    qDebug() << aryFuture.results();

    std::set<int> setDatas = { 1, 2, 3, 4, 5 };
    auto setFuture = QtConcurrent::mapped(setDatas, square);
    setFuture.waitForFinished();
    for (auto data : setDatas) {
        qDebug() << data;
    }
    qDebug() << setFuture.results();

    // error: no match for call to '(QtConcurrent::FunctionWrapper1<int, int>) (std::pair<const int, int>&)
    // std::map<int, int> mapDatas = { { 1, 1 }, { 2, 2 }, { 3, 3 } };
    // auto mapFuture = QtConcurrent::mapped(mapDatas, square);
    // qDebug() << mapDatas << mapFuture.results();

    return a.exec();
}

QtConcurrent::filter和filtered

这两个函数就是对容器中的数据进行筛选,filter是直接修改原始容器中的数据,而filtered是创建新容器来存储结果。

使用案例

bool isEven(int num)
{
    return num % 2 == 0;
}

int main(int argc, char* argv[])
{
    QVector<int> data = { 1, 2, 3, 4, 5, 6 };

    // 使用filter筛选数据
    QFuture<void> filterFuture = QtConcurrent::filter(data, isEven);
    filterFuture.waitForFinished();
    for (int value : data) {
        qDebug() << "Filtered value:" << value;
    }

    // 使用filtered筛选数据并获取结果
    QFuture<int> filteredFuture = QtConcurrent::filtered(data, isEven);
    filteredFuture.waitForFinished();
    QList<int> filteredResult = filteredFuture.results();
    for (int value : filteredResult) {
        qDebug() << "Filtered result value:" << value;
    }

    return 0;
}

这两个接口具体支持哪些类型的容器可以参考QtConcurrent::mapmapped系列章节内容。

QtConcurrent::filteredReduced

QtConcurrent::filteredReduced函数有两部操作,第一步是先过滤数据,第二步是将过滤的数据整合成一个值。函数原型:

template <typename ResultType, typename Sequence, typename KeepFunctor, typename ReduceFunctor>
QFuture<ResultType> filteredReduced(const Sequence &sequence,
                                    KeepFunctor keep,
                                    ReduceFunctor reduce,
                                    ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
  • sequence:原始容器数据
  • keep:这是一个过滤函数,用来决定元素是否需要被处理的可调用对象。每个元素都会调用该函数,只有此函数返回true时,元素才会被后面的reduce处理。
  • reduce:是一个归约函数,就是把经过过滤的数据整合成一个值。
  • options:归约操作选项,用来控制归约操作的顺序:
    • UnorderedReduce:归约操作按照任意顺序进行(不排序),可能会提高并行处理的效率
    • OrderedReduce:归约操作按照原始容器的顺序进行
    • SequentialReduce:归约操作在单个线程中执行,目前版本是没有意义的,因为目前所有归约操作只能在一个线程中进行处理

使用案例

将字符串容器中以逗号结尾的元素拼接起来
std::random_device rd; // 用于获取种子
std::mt19937 gen(rd()); // 标准 mersenne_twister_engine 初始化
int generateRandomInteger(int min, int max)
{
    std::uniform_int_distribution<> distrib(min, max); // 定义一个分布

    return distrib(gen);
}

// 过滤函数:检查字符串是否以逗号结尾
bool endsWithComma(const QString& str)
{
    // 通过随机延时来改变每次过滤函数的执行时间
    QThread::msleep(generateRandomInteger(1, 500));
    return str.endsWith(",");
}

// 归约函数:将字符串拼接到结果中
void concatenateStrings(QString& result, const QString& str)
{
    result += str;
}

int main(int argc, char* argv[])
{
    // 创建一个包含字符串的 QStringList
    QStringList strings = { "hello,", "world", "example,", "test", "code," };
    for (int i = 10; i < 99; i++) {
        if (i % 2 == 0)
            strings.append(QString::number(i) + ",");
        else
            strings.append(QString::number(i));
    }

    auto future = QtConcurrent::filteredReduced(strings, endsWithComma, concatenateStrings);
    future.waitForFinished();
    qDebug() << "DefaultReduce results:" << future.result() << Qt::endl;

    future = QtConcurrent::filteredReduced(strings, endsWithComma, concatenateStrings, QtConcurrent::UnorderedReduce);
    future.waitForFinished();
    qDebug() << "UnorderedReduce1 results:" << future.result() << Qt::endl;

    future = QtConcurrent::filteredReduced(strings, endsWithComma, concatenateStrings, QtConcurrent::UnorderedReduce);
    future.waitForFinished();
    qDebug() << "UnorderedReduce2 results:" << future.result() << Qt::endl;

    future = QtConcurrent::filteredReduced(strings, endsWithComma, concatenateStrings, QtConcurrent::OrderedReduce);
    future.waitForFinished();
    qDebug() << "OrderedReduce1 results:" << future.result() << Qt::endl;

    future = QtConcurrent::filteredReduced(strings, endsWithComma, concatenateStrings, QtConcurrent::OrderedReduce);
    future.waitForFinished();
    qDebug() << "OrderedReduce2 results:" << future.result() << Qt::endl;

    return 0;
}

上面代码在过滤函数中使用随机延时来使每次执行时元素过滤的顺序不一致,从而来验证UnorderedReduceOrderedReduce两个枚举值对归约函数的影响。结果如下:

DefaultReduce results: "code,22,28,24,example,32,12,18,hello,10,16,26,20,48,36,38,14,40,50,34,58,30,46,44,56,72,42,52,54,66,80,78,84,62,60,68,70,64,82,74,76,92,88,86,90,94,96,98," 

UnorderedReduce1 results: "24,code,26,14,hello,example,18,32,20,12,30,22,16,36,10,48,44,28,46,50,34,58,38,64,42,52,40,66,62,78,56,74,54,70,82,86,60,80,68,84,76,72,90,88,94,92,96,98," 

UnorderedReduce2 results: "hello,18,code,16,14,24,22,32,26,30,40,10,20,28,12,example,42,44,46,50,38,48,34,52,54,36,62,72,68,56,64,70,86,88,60,58,90,78,84,74,80,66,82,76,98,96,92,94," 

OrderedReduce1 results: "hello,example,code,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98," 

OrderedReduce2 results: "hello,example,code,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98," 

从结果可以看出,使用OrderedReduce枚举时,输出结果是一致的;而UnorderedReduce枚举的输出结果是不一致的。

源码解析

// Implementation of filter-reduce
template <typename ReducedResultType,
          typename Iterator,
          typename KeepFunctor,
          typename ReduceFunctor,
          typename Reducer = ReduceKernel<ReduceFunctor,
                                          ReducedResultType,
                                          typename qValueType<Iterator>::value_type> >
class FilteredReducedKernel : public IterateKernel<Iterator, ReducedResultType>
{
    ...

    bool runIteration(Iterator it, int index, ReducedResultType *) override
    {
        IntermediateResults<typename qValueType<Iterator>::value_type> results;
        results.begin = index;
        results.end = index + 1;

        if (keep(*it))
            results.vector.append(*it);

        reducer.runReduce(reduce, reducedResult, results);
        return false;
    }

    ...
};

这里直接展示QtConcurrent::filteredReduced函数底层最核心的代码,代码中FilteredReducedKernel::runIteration就是用来处理每一个元素的函数,函数内先调用了keep(过滤函数),如果返回true则把元素放到results中,然后再调用reducer.runReduce(归约函数)来处理结果。

QtConcurrent::mappedReduced

QtConcurrent::mappedReduced函数原型:

template <typename ResultType, typename Sequence, typename MapFunctor, typename ReduceFunctor>
QFuture<ResultType> mappedReduced(const Sequence &sequence,
                                  MapFunctor map,
                                  ReduceFunctor reduce,
                                  ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))

该函数和QtConcurrent::filteredReduced非常相似,唯一的区别就是第二个参数:MapFunctor map
filteredReduced函数是先经过KeepFunctor过滤,过滤后再经过ReduceFunctor整合处理成一个值;而mappedReduced函数是先经过MapFunctor处理,处理得到的返回值给ReduceFunctor整合处理成一个值。

这里我把MapFunctor称为映射函数,ReduceFunctor称为归约函数

使用案例

计算所有元素的平方和
// 平方处理
int square(int x)
{
    return x * x;
}

// 求和处理
void sum(int& result, int value)
{
    result += value;
}

int main(int argc, char* argv[])
{
    QVector<int> numbers = { 1, 2, 3, 4, 5 };
    auto result = QtConcurrent::mappedReduced(numbers, square, sum);
    result.waitForFinished(); // 等待操作完成
    qDebug() << "The sum of squares is:" << result.result();

    return 1;
}

源码解析


template <typename ReducedResultType,
          typename Iterator,
          typename MapFunctor,
          typename ReduceFunctor,
          typename Reducer = ReduceKernel<ReduceFunctor,
                                          ReducedResultType,
                                          typename MapFunctor::result_type> >
class MappedReducedKernel : public IterateKernel<Iterator, ReducedResultType>
{
    ...
    bool runIteration(Iterator it, int index, ReducedResultType *) override
    {
        IntermediateResults<typename MapFunctor::result_type> results;
        results.begin = index;
        results.end = index + 1;

        results.vector.append(map(*it));
        reducer.runReduce(reduce, reducedResult, results);
        return false;
    }
    ...
};

这里直接展示QtConcurrent::mappedReduced函数底层最核心的代码,代码中MappedReducedKernel::runIteration就是用来处理每一个元素的函数,函数内先调用了map(映射函数),把返回值放到results中,然后再调用reducer.runReduce(归约函数)来处理结果。

QtConcurrent::blockingXXXXX系列

blockingXXXXX系列的接口其实就是在原有的非阻塞并行处理接口(比如:filterfilteredmapmappedfilteredReducedmappedReduced等)的基础上,增加了阻塞调用线程直到所有操作完成的特性。
比如QtConcurrent::map(listDatas, square).waitForFinished();这行代码等同于:QtConcurrent::blockingMap(listDatas, square);

注意

前面介绍的接口中分为非阻塞接口和阻塞接口,阻塞接口就是以blockingXXX开头的接口,其他都是非阻塞接口。在实际的应用场景中,我们可能有时候需要非阻塞的,有时候又需要阻塞的,怎么区分呢?下面我们通过场景来举例说明:

场景一:有多个并行任务需要开启

两组数据求平方和

// 平方处理
int square(int x)
{
    QThread::sleep(1); // 模拟耗时操作
    return x * x;
}

// 求和处理
void sum(int& result, int value)
{
    result += value;
}

int main(int argc, char* argv[])
{
    QVector<int> numbers1 = { 1, 2, 3, 4, 5 };
    QVector<int> numbers2 = { 1, 2, 3, 4, 5 };
    auto result1 = QtConcurrent::mappedReduced(numbers1, square, sum);
    auto result2 = QtConcurrent::mappedReduced(numbers2, square, sum);

    result1.waitForFinished();
    result2.waitForFinished();
    qDebug() << "The sum of squares is:" << result2.result() + result2.result();

    return 1;
}

场景二:在并行任务处理过程中需要处理其他业务

例如:在数据求平方和的过程中处理其他业务

// 平方处理
int square(int x)
{
    QThread::sleep(1); // 模拟耗时操作
    return x * x;
}

// 求和处理
void sum(int& result, int value)
{
    result += value;
}

int main(int argc, char* argv[])
{
    QVector<int> numbers = { 1, 2, 3, 4, 5 };
    auto result = QtConcurrent::mappedReduced(numbers, square, sum);

    while (result.isFinished()) {
        // 模拟处理其他业务的耗时操作
        QThread::sleep(1);
    }

    qDebug() << "The sum of squares is:" << result.result();

    return 1;
}

场景三:需要并行任务处理的结果作为其他业务的输入

例如:数据求平方和的结果作为输出

// 平方处理
int square(int x)
{
    QThread::sleep(1); // 模拟耗时操作
    return x * x;
}

// 求和处理
void sum(int& result, int value)
{
    result += value;
}

int main(int argc, char* argv[])
{
    QVector<int> numbers = { 1, 2, 3, 4, 5 };
    auto result = QtConcurrent::blockingMappedReduced(numbers, square, sum);
    
    // 平方和的结果作为其他业务(输出字符串)的输入
    qDebug() << "The sum of squares is:" << result;

    return 1;
}

标签:map,square,Qt,int,waitForFinished,QtConcurrent,qDebug,多线程
From: https://blog.csdn.net/LeoLei8060/article/details/139680927

相关文章

  • 深入理解Qt的隐式共享机制
    在Qt中,一个关键的性能优化特性是其数据结构的隐式共享机制,这在Qt的文档和API中常被称为“隐式共享”或“写时复制(Copy-On-Write,COW)”。本文将详细介绍这一机制,并通过QString类的实现代码和相应的反汇编代码来阐释其工作原理。隐式共享的定义和优点隐式共享是一种内存管......
  • Java_多线程:实现多线程
    Java中实现多线程的常用方式:继承Thread类实现Runnable接口实现Callable接口(JDK>=1.5)线程池方式创建实现Runnable接口与Callable接口的区别实现Runnable接口和Callable接口的方式基本相同,不过Callable接口里定义的方法返回值,可以声明抛出异常。Runnable和Callable与......
  • java多线程-锁的介绍
    多线程中常用锁一、锁的概念二、锁的类型2.1互斥锁(也称排它锁)2.1.1Synchronized和Lock2.1.2ReentrantLock(可重入锁)2.1.3公平锁2.1.4非公平锁2.1.5中断锁2.2共享锁2.3读写锁三、悲观锁和乐观锁3.1悲观锁3.2乐观锁3.3CAS算法四、锁竞争一、锁的概念在多......
  • 在多线程并发操作中处理大量文件时,以下是一些关键的底层原理和技术:
    在多线程并发操作中处理大量文件时,以下是一些关键的底层原理和技术:1.文件句柄管理每个线程需要独立地管理文件句柄,文件句柄是操作系统提供的用于标识和访问文件的资源。在Windows环境下,使用CreateFile函数可以打开文件并获得文件句柄。每个文件句柄具有其自己的上下文和状态,......
  • Qt/C++开发经验小技巧296-300
    使用QDir::setCurrent设置当前目录后,会影响程序中的所有相对目录的执行,导致可能的意外发生,一般相对目录都默认是可执行文件所在目录,所以如果程序中为了特殊处理临时调用了QDir::setCurrent设置过相对目录,建议处理完成以后立即切换回来。QDir::setCurrent("f:/");QImageimg(":......
  • 深入理解 C++11 多线程编程:从入门到实践
    C++多线程编程是指使用C++提供的多线程库来并行执行代码块,从而提高程序的性能和响应能力。C++11标准引入了多线程支持,使得在C++中进行多线程编程变得更加容易和直观。以下是C++多线程编程的基本知识,并附有例子代码。多线程的基本概念线程(Thread):线程是进程中的一个执......
  • C#的多线程UI窗体控件显示方案 - 开源研究系列文章
          上次编写了《LUAgent服务器端工具》这个应用,然后里面需要新启动一个线程去对文件进行上传到FTP服务器,但是新线程里无法对应用主线程UI的内容进行更改,所以就需要在线程里设置主UI线程里控件信息的方法,于是就有了此博文。此文记录的是一种高级用法。      为了......
  • Qt QTableView设置自适应行高、列宽、行样式
    1、QTableView设置自适应行高ui->tableViewMonitor->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);//自动设置行高2、QTableView设置自适应列宽ui->tableViewMonitor->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents)......
  • Qt QTableWidget resizeRowsToContents非常慢
    QTableWidget是Qt框架中的一个表格控件,用于显示和编辑表格数据。resizeRowsToContents是QTableWidget的一个函数,用于自动调整表格行的高度以适应内容。该函数在某些情况下可能会导致性能问题,特别是当表格中的行数较多或者表格中的内容较复杂时。这是因为该函数需要遍......
  • 【嵌入式Linux】<总览> 多线程(更新中)
    文章目录前言一、多线程1.概述2.创建线程3.线程退出4.线程回收5.线程分离6.线程取消7.线程的ID比较二、线程同步1.概述2.互斥锁3.死锁4.读写锁5.条件变量6.信号量三、线程池前言记录学习多线程的知识重点与难点,若涉及版权问题请联系本人删除!......