QtConcurrent
The QtConcurrent namespace provides high-level APIs that make it possible to write multi-threaded programs without using low-level threading primitives.
QtConcurrent名称空间提供了高级api,可以在不使用低级线程原语的情况下编写多线程程序
1、Concurrent Map and Map-Reduce
The QtConcurrent::map(), QtConcurrent::mapped() and QtConcurrent::mappedReduced() functions run computations in parallel on the items in a sequence such as a QList or a QVector. QtConcurrent::map() modifies a sequence in-place, QtConcurrent::mapped() returns a new sequence containing the modified content, and QtConcurrent::mappedReduced() returns a single result.
QtConcurrent::map(), QtConcurrent::mapped()和QtConcurrent::mappedReduced()函数并行地对序列中的条目(如QList或QVector)执行计算。 QtConcurrent::map()原地修改序列,QtConcurrent::mapped()返回一个包含修改内容的新序列,而QtConcurrent::mappedReduced()返回单个结果。
Each of the above functions has a blocking variant that returns the final result instead of a QFuture. You use them in the same way as the asynchronous variants.
上面的每个函数都有一个阻塞变体,它返回最终结果而不是QFuture。 使用它们的方式与异步变量相同。
QList<QImage> images = ...;
// each call blocks until the entire operation is finished
QList<QImage> future = QtConcurrent::blockingMapped(images, scaled);
QtConcurrent::blockingMap(images, scale);
QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);
Note that the result types above are not QFuture objects, but real result types (in this case, QList<QImage> and QImage).
注意,上面的结果类型不是QFuture对象,而是实际的结果类型(在本例中,是QList<QImage>和QImage)。
Concurrent Map
QtConcurrent::mapped() takes an input sequence and a map function. This map function is then called for each item in the sequence, and a new sequence containing the return values from the map function is returned.
mapped()接受一个输入序列和一个映射函数。 然后对序列中的每一项调用这个map函数,并返回一个包含该map函数返回值的新序列。
The map function must be of the form:
map函数必须为以下形式:
U function(const T &t);
T and U can be any type (and they can even be the same type), but T must match the type stored in the sequence. The function returns the modified or mapped content.
T和U可以是任何类型(它们甚至可以是相同的类型),但是T必须匹配存储在序列中的类型。 该函数返回修改或映射的内容。
This example shows how to apply a scale function to all the items in a sequence:
这个例子展示了如何将scale函数应用到序列中的所有项:
QImage scaled(const QImage &image) { return image.scaled(100, 100); } QList<QImage> images = ...; QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scaled);
The results of the map are made available through QFuture. See the QFuture and QFutureWatcher documentation for more information on how to use QFuture in your applications.
地图的结果可以通过QFuture获得。 有关如何在应用程序中使用QFuture的更多信息,请参阅QFuture和QFutureWatcher文档。
If you want to modify a sequence in-place, use QtConcurrent::map(). The map function must then be of the form:
如果您想就地修改序列,请使用QtConcurrent::map()。 然后,map函数必须是以下形式:
U function(T &t);
Note that the return value and return type of the map function are not used.
注意,没有使用map函数的返回值和返回类型。
Using QtConcurrent::map() is similar to using QtConcurrent::mapped():
使用QtConcurrent::map()类似于使用QtConcurrent::mapped():
void scale(QImage &image) { image = image.scaled(100, 100); } QList<QImage> images = ...; QFuture<void> future = QtConcurrent::map(images, scale);
Since the sequence is modified in place, QtConcurrent::map() does not return any results via QFuture. However, you can still use QFuture and QFutureWatcher to monitor the status of the map.
因为序列被修改了,所以QtConcurrent::map()不会通过QFuture返回任何结果。 但是,您仍然可以使用QFuture和QFutureWatcher来监视地图的状态。
QFuture<void> QtConcurrent::map(Sequence &sequence, MapFunction function) QFuture<void> QtConcurrent::map(Iterator begin, Iterator end, MapFunction function)
Calls function once for each item in sequence. The function is passed a reference to the item, so that any modifications done to the item will appear in sequence.
依次为每个项调用一次函数。 函数会传递一个对该项的引用,因此对该项所做的任何修改都将按顺序出现。
Concurrent Map-Reduce
QFuture<T> QtConcurrent::mappedReduced(const Sequence &sequence, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce) QFuture<T> QtConcurrent::mappedReduced(ConstIterator begin, ConstIterator end, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)
QtConcurrent::mappedReduced() is similar to QtConcurrent::mapped(), but instead of returning a sequence with the new results, the results are combined into a single value using a reduce function.
QtConcurrent::mappedReduced()类似于QtConcurrent::mapped(),但是不是返回一个包含新结果的序列,而是使用reduce函数将结果组合成一个单独的值。
The reduce function must be of the form:
reduce函数的形式必须是:
V function(T &result, const U &intermediate)
T is the type of the final result, U is the return type of the map function. Note that the return value and return type of the reduce function are not used.
T是最终结果的类型,U是映射函数的返回类型。 注意,reduce函数的返回值和返回类型没有被使用。
Call QtConcurrent::mappedReduced() like this:
void addToCollage(QImage &collage, const QImage &thumbnail) { QPainter p(&collage); static QPoint offset = QPoint(0, 0); p.drawImage(offset, thumbnail); offset += ...; } QList<QImage> images = ...; QFuture<QImage> collage = QtConcurrent::mappedReduced(images, scaled, addToCollage);
The reduce function will be called once for each result returned by the map function, and should merge the intermediate into the result variable. QtConcurrent::mappedReduced() guarantees that only one thread will call reduce at a time, so using a mutex to lock the result variable is not necessary. The QtConcurrent::ReduceOptions enum provides a way to control the order in which the reduction is done. If QtConcurrent::UnorderedReduce is used (the default), the order is undefined, while QtConcurrent::OrderedReduce ensures that the reduction is done in the order of the original sequence.
对于map函数返回的每个结果,reduce函数将被调用一次,并且应该将中间体合并到结果变量中。QtConcurrent::mappedReduced()保证一次只有一个线程调用reduce,所以不需要使用互斥锁来锁定结果变量。QtConcurrent::ReduceOptions枚举提供了一种方法来控制减少完成的顺序。如果使用了QtConcurrent::UnorderedReduce(默认值),则顺序是未定义的,而QtConcurrent::OrderedReduce确保还原是按照原始序列的顺序进行的。
Additional API Features
Using Iterators instead of Sequence
Each of the above functions has a variant that takes an iterator range instead of a sequence. You use them in the same way as the sequence variants:
上面的每个函数都有一个变量,该变量接受迭代器范围而不是序列。你可以像使用序列变量一样使用它们:
QList<QImage> images = ...; QFuture<QImage> thumbnails = QtConcurrent::mapped(images.constBegin(), images.constEnd(), scaled); // map in-place only works on non-const iterators QFuture<void> future = QtConcurrent::map(images.begin(), images.end(), scale); QFuture<QImage> collage = QtConcurrent::mappedReduced(images.constBegin(), images.constEnd(), scaled, addToCollage);
Blocking Variants
Each of the above functions has a blocking variant that returns the final result instead of a QFuture. You use them in the same way as the asynchronous variants.
上面的每个函数都有一个阻塞变体,它返回最终结果而不是QFuture。使用它们的方式与异步变体相同。
QList<QImage> images = ...; // each call blocks until the entire operation is finished QList<QImage> future = QtConcurrent::blockingMapped(images, scaled); QtConcurrent::blockingMap(images, scale); QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);
Note that the result types above are not QFuture objects, but real result types (in this case, QList<QImage> and QImage).
注意,上面的结果类型不是QFuture对象,而是实际的结果类型(在本例中,是QList<QImage>和QImage)。
Using Member Functions
QtConcurrent::map(), QtConcurrent::mapped(), and QtConcurrent::mappedReduced() accept pointers to member functions. The member function class type must match the type stored in the sequence:
QtConcurrent::map(), QtConcurrent::mapped()和QtConcurrent::mappedReduced()接受指向成员函数的指针。成员函数类类型必须与序列中存储的类型匹配:
// squeeze all strings in a QStringList QStringList strings = ...; QFuture<void> squeezedStrings = QtConcurrent::map(strings, &QString::squeeze); // swap the rgb values of all pixels on a list of images QList<QImage> images = ...; QFuture<QImage> bgrImages = QtConcurrent::mapped(images, &QImage::rgbSwapped); // create a set of the lengths of all strings in a list QStringList strings = ...; QFuture<QSet<int> > wordLengths = QtConcurrent::mappedReduced(strings, &QString::length, &QSet<int>::insert);
Note that when using QtConcurrent::mappedReduced(), you can mix the use of normal and member functions freely:
注意,当使用QtConcurrent::mappedReduced()时,你可以自由地混合使用普通函数和成员函数:
// can mix normal functions and member functions with QtConcurrent::mappedReduced() // compute the average length of a list of strings extern void computeAverage(int &average, int length); QStringList strings = ...; QFuture<int> averageWordLength = QtConcurrent::mappedReduced(strings, &QString::length, computeAverage); // create a set of the color distribution of all images in a list extern int colorDistribution(const QImage &string); QList<QImage> images = ...; QFuture<QSet<int> > totalColorDistribution = QtConcurrent::mappedReduced(images, colorDistribution, QSet<int>::insert);
Using Function Objects
QtConcurrent::map(), QtConcurrent::mapped(), and QtConcurrent::mappedReduced() accept function objects, which can be used to add state to a function call. The result_type typedef must define the result type of the function call operator:
QtConcurrent::map(), QtConcurrent::mapped()和QtConcurrent::mappedReduced()接受函数对象,可以用来给函数调用添加状态。result_type类型定义必须定义函数调用操作符的结果类型:
struct Scaled { Scaled(int size) : m_size(size) { } typedef QImage result_type; QImage operator()(const QImage &image) { return image.scaled(m_size, m_size); } int m_size; }; QList<QImage> images = ...; QFuture<QImage> thumbnails = QtConcurrent::mapped(images, Scaled(100));
Wrapping Functions that Take Multiple Arguments
If you want to use a map function that takes more than one argument you can use a lambda function or std::bind() to transform it onto a function that takes one argument.
如果你想使用一个带有多个参数的map函数,你可以使用lambda函数或std::bind()将其转换为一个带有一个参数的函数。
As an example, we'll use QImage::scaledToWidth():
QImage QImage::scaledToWidth(int width, Qt::TransformationMode) const;
scaledToWidth takes three arguments (including the "this" pointer) and can't be used with QtConcurrent::mapped() directly, because QtConcurrent::mapped() expects a function that takes one argument. To use QImage::scaledToWidth() with QtConcurrent::mapped() we have to provide a value for the width and the transformation mode:
scaledToWidth接受三个参数(包括“this”指针),不能直接与QtConcurrent::mapped()一起使用,因为QtConcurrent::mapped()需要一个接受一个参数的函数。要使用QImage::scaledToWidth()和QtConcurrent::mapped(),我们必须为宽度和转换模式提供一个值:
QList<QImage> images = ...; QFuture<QImage> thumbnails = QtConcurrent::mapped(images, [](const QImage &img) { return img.scaledToWidth(100, Qt::SmoothTransformation); });
2、QFuture Class
The QFuture class represents the result of an asynchronous computation.
QFuture类表示异步计算的结果。
To start a computation, use one of the APIs in the Qt Concurrent framework.
要开始一个计算,使用Qt Concurrent框架中的一个api。
QFuture allows threads to be synchronized against one or more results which will be ready at a later point in time. The result can be of any type that has a default constructor and a copy constructor. If a result is not available at the time of calling the result(), resultAt(), or results() functions, QFuture will wait until the result becomes available. You can use the isResultReadyAt() function to determine if a result is ready or not. For QFuture objects that report more than one result, the resultCount() function returns the number of continuous results. This means that it is always safe to iterate through the results from 0 to resultCount().
QFuture允许线程对一个或多个结果进行同步,这些结果将在稍后的时间点准备好。结果可以是具有默认构造函数和复制构造函数的任何类型。如果一个结果在调用result()、resultAt()或results()函数时不可用,QFuture将等待直到结果可用。可以使用isResultReadyAt()函数来确定一个结果是否准备好了。对于报告多个结果的QFuture对象,resultCount()函数返回连续结果的数量。这意味着迭代从0到resultCount()的结果总是安全的。
QFuture provides a Java-style iterator (QFutureIterator) and an STL-style iterator (QFuture::const_iterator). Using these iterators is another way to access results in the future.
QFuture提供了一个java风格的迭代器(qfutuator)和一个stl风格的迭代器(QFuture::const_iterator)。使用这些迭代器是将来访问结果的另一种方式。
QFuture also offers ways to interact with a runnning computation. For instance, the computation can be canceled with the cancel() function. To pause the computation, use the setPaused() function or one of the pause(), resume(), or togglePaused() convenience functions. Be aware that not all asynchronous computations can be canceled or paused. For example, the future returned by QtConcurrent::run() cannot be canceled; but the future returned by QtConcurrent::mappedReduced() can.
QFuture还提供了与正在运行的计算交互的方法。例如,可以使用cancel()函数取消计算。要暂停计算,请使用setPaused()函数或pause()、resume()或togglePaused()方便函数之一。请注意,并不是所有的异步计算都可以取消或暂停。例如,QtConcurrent::run()返回的future不能被取消;但是由QtConcurrent::mappedReduced()返回的future可以。
Progress information is provided by the progressValue(), progressMinimum(), progressMaximum(), and progressText() functions. The waitForFinished() function causes the calling thread to block and wait for the computation to finish, ensuring that all results are available.
进度信息由progressValue()、progressMinimum()、progressMaximum()和progressText()函数提供。waitForFinished()函数导致调用线程阻塞并等待计算完成,以确保所有结果都可用。
The state of the computation represented by a QFuture can be queried using the isCanceled(), isStarted(), isFinished(), isRunning(), or isPaused() functions.
QFuture表示的计算状态可以使用isCanceled()、isStarted()、isFinished()、isRunning()或isPaused()函数来查询。
QFuture is a lightweight reference counted class that can be passed by value.
QFuture是一个轻量级引用计数类,可以按值传递。
QFuture<void> is specialized to not contain any of the result fetching functions. Any QFuture<T> can be assigned or copied into a QFuture<void> as well. This is useful if only status or progress information is needed - not the actual result data.
QFuture<void>专门化为不包含任何结果获取函数。任何QFuture<T> 可以被赋值或复制到QFuture<void>,如果只需要状态或进度信息(而不是实际的结果数据),这是很有用的。
To interact with running tasks using signals and slots, use QFutureWatcher.
3、QFutureWatcher Class
The QFutureWatcher class allows monitoring a QFuture using signals and slots.
QFutureWatcher类允许使用信号和槽来监视QFuture。
QFutureWatcher provides information and notifications about a QFuture. Use the setFuture() function to start watching a particular QFuture. The future() function returns the future set with setFuture().
QFutureWatcher提供关于QFuture的信息和通知。 使用setFuture()函数开始监视特定的QFuture。 函数的作用是:通过setFuture()返回未来集合。
For convenience, several of QFuture's functions are also available in QFutureWatcher: progressValue(), progressMinimum(), progressMaximum(), progressText(), isStarted(), isFinished(), isRunning(), isCanceled(), isPaused(), waitForFinished(), result(), and resultAt(). The cancel(), setPaused(), pause(), resume(), and togglePaused() functions are slots in QFutureWatcher.
为了方便起见,QFuture的几个函数也可以在QFutureWatcher中使用:progressValue(), progressMinimum(), progressMaximum(), progressText(), isStarted(), isFinished(), isRunning(), isCanceled(), isPaused(), waitForFinished(), result(),和resultAt()。 cancel()、setPaused()、pause()、resume()和togglePaused()函数都是QFutureWatcher中的槽。
Status changes are reported via the started(), finished(), canceled(), paused(), resumed(), resultReadyAt(), and resultsReadyAt() signals. Progress information is provided from the progressRangeChanged(), void progressValueChanged(), and progressTextChanged() signals.
状态变化通过started()、finished()、canceled()、paused()、resumed()、resultReadyAt()和resultsReadyAt()信号来报告。 进度信息由progressRangeChanged()、void progressValueChanged()和progressTextChanged()信号提供。
Throttling control is provided by the setPendingResultsLimit() function. When the number of pending resultReadyAt() or resultsReadyAt() signals exceeds the limit, the computation represented by the future will be throttled automatically. The computation will resume once the number of pending signals drops below the limit.
调节控制是由setPendingResultsLimit()函数提供的。 当等待的resultReadyAt()或resultsReadyAt()信号的数量超过限制时,由future表示的计算将被自动控制。 一旦待处理信号的数量低于限制,计算将继续进行。
Example: Starting a computation and getting a slot callback when it's finished:
示例:开始计算并在计算完成后获得一个槽回调:
// Instantiate the objects and connect to the finished signal. MyClass myObject; QFutureWatcher<int> watcher; connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished())); // Start the computation. QFuture<int> future = QtConcurrent::run(...); watcher.setFuture(future);
Cancels the asynchronous computation represented by the future(). Note that the cancelation is asynchronous. Use waitForFinished() after calling cancel() when you need synchronous cancelation.
取消future()表示的异步计算。 注意,取消是异步的。 当你需要同步取消时,在调用cancel()之后使用waitForFinished()。
Currently available results may still be accessed on a canceled QFuture, but new results will not become available after calling this function. Also, this QFutureWatcher will not deliver progress and result ready signals once canceled. This includes the progressValueChanged(), progressRangeChanged(), progressTextChanged(), resultReadyAt(), and resultsReadyAt() signals.
当前可用的结果仍然可以在取消的QFuture上访问,但是在调用这个函数后,新的结果将不可用。 此外,一旦取消,这个QFutureWatcher将不会交付进度和结果就绪信号。 这包括progressValueChanged(), progressRangeChanged(), progressTextChanged(), resultReadyAt()和resultsReadyAt()信号。
Be aware that not all asynchronous computations can be canceled or paused. For example, the future returned by QtConcurrent::run() cannot be canceled; but the future returned by QtConcurrent::mappedReduced() can.
请注意,并不是所有的异步计算都可以取消或暂停。 例如,QtConcurrent::run()返回的future不能被取消; 但是由QtConcurrent::mappedReduced()返回的future可以。
QFutureWatcher<void> is specialized to not contain any of the result fetching functions. Any QFuture<T> can be watched by a QFutureWatcher<void> as well. This is useful if only status or progress information is needed; not the actual result data.
QFutureWatcher<void>被专门用于不包含任何结果抓取函数。 任何QFuture<T>也可以被QFutureWatcher<void>监视。 如果只需要状态或进度信息,这很有用; 不是实际的结果数据。
4、QFutureSynchronizer Class
The QFutureSynchronizer class is a convenience class that simplifies QFuture synchronization.
QFutureSynchronizer类是一个简化了QFuture同步的便利类。
QFutureSynchronizer is a template class that simplifies synchronization of one or more QFuture objects. Futures are added using the addFuture() or setFuture() functions. The futures() function returns a list of futures. Use clearFutures() to remove all futures from the QFutureSynchronizer.
QFutureSynchronizer是一个模板类,它简化了一个或多个QFuture对象的同步。使用addFuture()或setFuture()函数添加future。函数的作用是:返回一个期货列表。使用clearFutures()从QFutureSynchronizer中删除所有的期货。
The waitForFinished() function waits for all futures to finish. The destructor of QFutureSynchronizer calls waitForFinished(), providing an easy way to ensure that all futures have finished before returning from a function:
waitForFinished()函数等待所有的期货交易完成。QFutureSynchronizer的析构函数调用waitForFinished(),提供了一种简单的方法来确保所有的期货在从函数返回之前已经完成:
void someFunction() { QFutureSynchronizer<void> synchronizer; ... synchronizer.addFuture(QtConcurrent::run(anotherFunction)); synchronizer.addFuture(QtConcurrent::map(list, mapFunction)); return; // QFutureSynchronizer waits for all futures to finish }
The behavior of waitForFinished() can be changed using the setCancelOnWait() function. Calling setCancelOnWait(true) will cause waitForFinished() to cancel all futures before waiting for them to finish. You can query the status of the cancel-on-wait feature using the cancelOnWait() function.
waitForFinished()的行为可以通过setCancelOnWait()函数来改变。调用setCancelOnWait(true)将导致waitForFinished()在等待它们完成之前取消所有的期货。cancelOnWait()函数用于查询等待取消特性的状态。
QtConcurrent Progress Dialog Example
The QtConcurrent Progress Dialog example shows how to use the QFutureWatcher class to monitor the progress of a long-running operation.
QtConcurrent Progress Dialog示例展示了如何使用QFutureWatcher类来监视一个长时间运行的操作的进度。
#include <QtWidgets> #include <QtConcurrent> #include <functional> using namespace QtConcurrent; int main(int argc, char **argv) { QApplication app(argc, argv); const int iterations = 20; // Prepare the vector. QVector<int> vector; for (int i = 0; i < iterations; ++i) vector.append(i); // Create a progress dialog. QProgressDialog dialog; dialog.setLabelText(QString("Progressing using %1 thread(s)...").arg(QThread::idealThreadCount())); // Create a QFutureWatcher and connect signals and slots. QFutureWatcher<void> futureWatcher; QObject::connect(&futureWatcher, &QFutureWatcher<void>::finished, &dialog, &QProgressDialog::reset); QObject::connect(&dialog, &QProgressDialog::canceled, &futureWatcher, &QFutureWatcher<void>::cancel); QObject::connect(&futureWatcher, &QFutureWatcher<void>::progressRangeChanged, &dialog, &QProgressDialog::setRange); QObject::connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &dialog, &QProgressDialog::setValue); // Our function to compute std::function<void(int&)> spin = [](int &iteration) { const int work = 1000 * 1000 * 40; volatile int v = 0; for (int j = 0; j < work; ++j) ++v; qDebug() << "iteration" << iteration << "in thread" << QThread::currentThreadId(); }; // Start the computation. futureWatcher.setFuture(QtConcurrent::map(vector, spin)); // Display the dialog and start the event loop. dialog.exec(); futureWatcher.waitForFinished(); // Query the future to check if was canceled. qDebug() << "Canceled?" << futureWatcher.future().isCanceled(); }
QtConcurrent Word Count Example
The QtConcurrent Word Count example demonstrates the use of the map-reduce algorithm when applied to the problem of counting words in a collection of files.
QtConcurrent Word Count示例演示了当应用于计算文件集合中的单词数量时,如何使用map-reduce算法。
#include <QList> #include <QMap> #include <QTextStream> #include <QString> #include <QStringList> #include <QDir> #include <QTime> #include <QApplication> #include <QDebug> #include <qtconcurrentmap.h> using namespace QtConcurrent; /* Utility function that recursivily searches for files. */ QStringList findFiles(const QString &startDir, const QStringList &filters) { QStringList names; QDir dir(startDir); const auto files = dir.entryList(filters, QDir::Files); for (const QString &file : files) names += startDir + '/' + file; const auto subdirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); for (const QString &subdir : subdirs) names += findFiles(startDir + '/' + subdir, filters); return names; } typedef QMap<QString, int> WordCount; /* Single threaded word counter function. */ WordCount singleThreadedWordCount(const QStringList &files) { WordCount wordCount; for (const QString &file : files) { QFile f(file); f.open(QIODevice::ReadOnly); QTextStream textStream(&f); while (!textStream.atEnd()) { const auto words = textStream.readLine().split(' '); for (const QString &word : words) wordCount[word] += 1; } } return wordCount; } // countWords counts the words in a single file. This function is // called in parallel by several threads and must be thread // safe. WordCount countWords(const QString &file) { QFile f(file); f.open(QIODevice::ReadOnly); QTextStream textStream(&f); WordCount wordCount; while (!textStream.atEnd()) { const auto words = textStream.readLine().split(' '); for (const QString &word : words) wordCount[word] += 1; } return wordCount; } // reduce adds the results from map to the final // result. This functor will only be called by one thread // at a time. void reduce(WordCount &result, const WordCount &w) { QMapIterator<QString, int> i(w); while (i.hasNext()) { i.next(); result[i.key()] += i.value(); } } int main(int argc, char** argv) { QApplication app(argc, argv); qDebug() << "finding files..."; QStringList files = findFiles("../../", QStringList() << "*.cpp" << "*.h"); qDebug() << files.count() << "files"; qDebug() << "warmup"; { WordCount total = singleThreadedWordCount(files); } qDebug() << "warmup done"; int singleThreadTime = 0; { QTime time; time.start(); WordCount total = singleThreadedWordCount(files); singleThreadTime = time.elapsed(); qDebug() << "single thread" << singleThreadTime; } int mapReduceTime = 0; { QTime time; time.start(); WordCount total = mappedReduced(files, countWords, reduce); mapReduceTime = time.elapsed(); qDebug() << "MapReduce" << mapReduceTime; } qDebug() << "MapReduce speedup x" << ((double)singleThreadTime - (double)mapReduceTime) / (double)mapReduceTime + 1; }
5、Concurrent Run
The QtConcurrent::run() function runs a function in a separate thread. The return value of the function is made available through the QFuture API.
QtConcurrent::run()函数在单独的线程中运行一个函数。函数的返回值可以通过QFuture API获得。
Running a Function in a Separate Thread 在单独的线程中运行函数
To run a function in another thread, use QtConcurrent::run():
要在另一个线程中运行一个函数,使用QtConcurrent::run():
extern void aFunction(); QFuture<void> future = QtConcurrent::run(aFunction);
This will run aFunction in a separate thread obtained from the default QThreadPool. You can use the QFuture and QFutureWatcher classes to monitor the status of the function.
这将在从默认QThreadPool获得的单独线程中运行function。您可以使用QFuture和QFutureWatcher类来监视函数的状态。
To use a dedicated thread pool, you can pass the QThreadPool as the first argument:
要使用专用的线程池,你可以传递QThreadPool作为第一个参数:
extern void aFunction(); QThreadPool pool; QFuture<void> future = QtConcurrent::run(&pool, aFunction);
Passing Arguments to the Function 传递参数给函数
Passing arguments to the function is done by adding them to the QtConcurrent::run() call immediately after the function name. For example:
将参数传递给函数是通过将它们添加到QtConcurrent::run()调用中来完成的,该调用紧跟在函数名之后。例如:
extern void aFunctionWithArguments(int arg1, double arg2, const QString &string); int integer = ...; double floatingPoint = ...; QString string = ...; QFuture<void> future = QtConcurrent::run(aFunctionWithArguments, integer, floatingPoint, string);
A copy of each argument is made at the point where QtConcurrent::run() is called, and these values are passed to the thread when it begins executing the function. Changes made to the arguments after calling QtConcurrent::run() are not visible to the thread.
在QtConcurrent::run()被调用的地方复制每个参数,当线程开始执行函数时,这些值被传递给线程。调用QtConcurrent::run()后对参数的更改对线程是不可见的。
Returning Values from the Function 函数返回值
Any return value from the function is available via QFuture:
函数的任何返回值都可以通过QFuture获得:
extern QString functionReturningAString(); QFuture<QString> future = QtConcurrent::run(functionReturningAString); ... QString result = future.result();
As documented above, passing arguments is done like this:
如上所述,传递参数是这样的:
extern QString someFunction(const QByteArray &input); QByteArray bytearray = ...; QFuture<QString> future = QtConcurrent::run(someFunction, bytearray); ... QString result = future.result();
Note that the QFuture::result() function blocks and waits for the result to become available. Use QFutureWatcher to get notification when the function has finished execution and the result is available.
注意,QFuture::result()函数会阻塞并等待结果变为可用。使用QFutureWatcher在函数完成执行并且结果可用时获得通知。
Additional API Features
Using Member Functions
QtConcurrent::run() also accepts pointers to member functions. The first argument must be either a const reference or a pointer to an instance of the class. Passing by const reference is useful when calling const member functions; passing by pointer is useful for calling non-const member functions that modify the instance.
QtConcurrent::run()也接受指向成员函数的指针。第一个实参必须是const引用或指向类实例的指针。在调用const成员函数时,传递const引用很有用;通过指针传递对调用修改实例的非const成员函数很有用。
For example, calling QByteArray::split() (a const member function) in a separate thread is done like this:
例如,在一个单独的线程中调用QByteArray::split() (const成员函数)是这样的:
// call 'QList<QByteArray> QByteArray::split(char sep) const' in a separate thread QByteArray bytearray = "hello world"; QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ','); ... QList<QByteArray> result = future.result();
Calling a non-const member function is done like this:
调用非const成员函数是这样的:
// call 'void QImage::invertPixels(InvertMode mode)' in a separate thread QImage image = ...; QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba); ... future.waitForFinished(); // At this point, the pixels in 'image' have been inverted
Using Lambda Functions
Calling a lambda function is done like this:
调用lambda函数是这样的:
QFuture<void> future = QtConcurrent::run([=]() { // Code in this block will run in another thread }); ...
6、Concurrent Filter and Filter-Reduce
The QtConcurrent::filter(), QtConcurrent::filtered() and QtConcurrent::filteredReduced() functions filter items in a sequence such as a QList or a QVector in parallel. QtConcurrent::filter() modifies a sequence in-place, QtConcurrent::filtered() returns a new sequence containing the filtered content, and QtConcurrent::filteredReduced() returns a single result.
QtConcurrent::filter(), QtConcurrent::filtered()和QtConcurrent::filteredReduced()函数并行地按顺序过滤条目,比如QList或QVector。QtConcurrent::filter()原地修改序列,QtConcurrent::filtered()返回一个包含过滤内容的新序列,而QtConcurrent::filteredReduced()返回单个结果。
Each of the above functions have a blocking variant that returns the final result instead of a QFuture. You use them in the same way as the asynchronous variants.
上面的每个函数都有一个阻塞变体,它返回最终结果而不是QFuture。使用它们的方式与异步变量相同。
QStringList strings = ...; // each call blocks until the entire operation is finished QStringList lowerCaseStrings = QtConcurrent::blockingFiltered(strings, allLowerCase); QtConcurrent::blockingFilter(strings, allLowerCase); QSet<QString> dictionary = QtConcurrent::blockingFilteredReduced(strings, allLowerCase, addToDictionary);
Note that the result types above are not QFuture objects, but real result types (in this case, QStringList and QSet<QString>).
注意,上面的结果类型不是QFuture对象,而是实际的结果类型(在本例中是QStringList和QSet<QString>)。
Concurrent Filter
QtConcurrent::filtered() takes an input sequence and a filter function. This filter function is then called for each item in the sequence, and a new sequence containing the filtered values is returned.
QtConcurrent::filtered()接受一个输入序列和一个过滤函数。然后对序列中的每一项调用这个筛选函数,并返回一个包含筛选值的新序列。
The filter function must be of the form:
filter函数必须为以下形式:
bool function(const T &t);
T must match the type stored in the sequence. The function returns true if the item should be kept, false if it should be discarded.
T必须匹配存储在序列中的类型。如果保留该项,则函数返回true,如果丢弃该项,则返回false。
This example shows how to keep strings that are all lower-case from a QStringList:
这个例子展示了如何保存QStringList中的小写字符串:
bool allLowerCase(const QString &string) { return string.lowered() == string; } QStringList strings = ...; QFuture<QString> lowerCaseStrings = QtConcurrent::filtered(strings, allLowerCase);
The results of the filter are made available through QFuture. See the QFuture and QFutureWatcher documentation for more information on how to use QFuture in your applications.
筛选器的结果可以通过QFuture获得。有关如何在应用程序中使用QFuture的更多信息,请参阅QFuture和QFutureWatcher文档。
If you want to modify a sequence in-place, use QtConcurrent::filter():
如果你想就地修改一个序列,使用QtConcurrent::filter():
QStringList strings = ...; QFuture<void> future = QtConcurrent::filter(strings, allLowerCase);
Since the sequence is modified in place, QtConcurrent::filter() does not return any results via QFuture. However, you can still use QFuture and QFutureWatcher to monitor the status of the filter.
因为这个序列被适当地修改了,所以QtConcurrent::filter()不会通过QFuture返回任何结果。但是,您仍然可以使用QFuture和QFutureWatcher来监视过滤器的状态。
Concurrent Filter-Reduce
QtConcurrent::filteredReduced() is similar to QtConcurrent::filtered(), but instead of returing a sequence with the filtered results, the results are combined into a single value using a reduce function.
QtConcurrent::filteredReduced()类似于QtConcurrent::filtered(),但是不是返回一个包含过滤结果的序列,而是使用reduce函数将这些结果组合成一个单独的值。
The reduce function must be of the form:
reduce函数的形式必须是:
V function(T &result, const U &intermediate)
T is the type of the final result, U is the type of items being filtered. Note that the return value and return type of the reduce function are not used.
T是最终结果的类型,U是被过滤项目的类型。注意,reduce函数的返回值和返回类型没有被使用。
Call QtConcurrent::filteredReduced() like this:
像这样调用QtConcurrent::filteredReduced():
void addToDictionary(QSet<QString> &dictionary, const QString &string) { dictionary.insert(string); } QStringList strings = ...; QFuture<QSet<QString> > dictionary = QtConcurrent::filteredReduced(strings, allLowerCase, addToDictionary);
The reduce function will be called once for each result kept by the filter function, and should merge the intermediate into the result variable. QtConcurrent::filteredReduced() guarantees that only one thread will call reduce at a time, so using a mutex to lock the result variable is not necessary. The QtConcurrent::ReduceOptions enum provides a way to control the order in which the reduction is done.
对于过滤函数保存的每个结果,reduce函数将被调用一次,并且应该将中间值合并到结果变量中。QtConcurrent::filteredReduced()保证一次只有一个线程会调用reduce,所以不需要使用互斥锁来锁定结果变量。QtConcurrent::ReduceOptions枚举提供了一种方法来控制减少完成的顺序。
Additional API Features
Using Iterators instead of Sequence
Each of the above functions has a variant that takes an iterator range instead of a sequence. You use them in the same way as the sequence variants:
上面的每个函数都有一个变量,该变量接受迭代器范围而不是序列。你可以像使用序列变量一样使用它们:
QStringList strings = ...; QFuture<QString> lowerCaseStrings = QtConcurrent::filtered(strings.constBegin(), strings.constEnd(), allLowerCase); // filter in-place only works on non-const iterators QFuture<void> future = QtConcurrent::filter(strings.begin(), strings.end(), allLowerCase); QFuture<QSet<QString> > dictionary = QtConcurrent::filteredReduced(strings.constBegin(), strings.constEnd(), allLowerCase, addToDictionary);
Using Member Functions
QtConcurrent::filter(), QtConcurrent::filtered(), and QtConcurrent::filteredReduced() accept pointers to member functions. The member function class type must match the type stored in the sequence:
QtConcurrent::filter(), QtConcurrent::filtered()和QtConcurrent::filteredReduced()接受指向成员函数的指针。成员函数类类型必须与序列中存储的类型匹配:
// keep only images with an alpha channel QList<QImage> images = ...; QFuture<void> alphaImages = QtConcurrent::filter(images, &QImage::hasAlphaChannel); // retrieve gray scale images QList<QImage> images = ...; QFuture<QImage> grayscaleImages = QtConcurrent::filtered(images, &QImage::isGrayscale); // create a set of all printable characters QList<QChar> characters = ...; QFuture<QSet<QChar> > set = QtConcurrent::filteredReduced(characters, &QChar::isPrint, &QSet<QChar>::insert);
Note that when using QtConcurrent::filteredReduced(), you can mix the use of normal and member functions freely:
注意,当使用QtConcurrent::filteredReduced()时,你可以自由地混合使用普通函数和成员函数:
// can mix normal functions and member functions with QtConcurrent::filteredReduced() // create a dictionary of all lower cased strings extern bool allLowerCase(const QString &string); QStringList strings = ...; QFuture<QSet<int> > averageWordLength = QtConcurrent::filteredReduced(strings, allLowerCase, QSet<QString>::insert); // create a collage of all gray scale images extern void addToCollage(QImage &collage, const QImage &grayscaleImage); QList<QImage> images = ...; QFuture<QImage> collage = QtConcurrent::filteredReduced(images, &QImage::isGrayscale, addToCollage);
Using Function Objects
QtConcurrent::filter(), QtConcurrent::filtered(), and QtConcurrent::filteredReduced() accept function objects, which can be used to add state to a function call. The result_type typedef must define the result type of the function call operator:
QtConcurrent::filter()、QtConcurrent::filtered()和QtConcurrent::filteredReduced()接受函数对象,可以用来给函数调用添加状态。result_type类型定义必须定义函数调用操作符的结果类型:
struct StartsWith { StartsWith(const QString &string) : m_string(string) { } typedef bool result_type; bool operator()(const QString &testString) { return testString.startsWith(m_string); } QString m_string; }; QList<QString> strings = ...; QFuture<QString> fooString = QtConcurrent::filtered(images, StartsWith(QLatin1String("Foo")));
Wrapping Functions that Take Multiple Arguments
If you want to use a filter function takes more than one argument, you can use a lambda function or std::bind() to transform it onto a function that takes one argument.
如果你想使用一个带有多个参数的filter函数,你可以使用lambda函数或std::bind()将其转换为一个带有一个参数的函数。
As an example, we use QString::contains():
例如,我们使用QString::contains():
bool QString::contains(const QRegularExpression ®exp) const;
QString::contains() takes 2 arguments (including the "this" pointer) and can't be used with QtConcurrent::filtered() directly, because QtConcurrent::filtered() expects a function that takes one argument. To use QString::contains() with QtConcurrent::filtered() we have to provide a value for the regexp argument:
QString::contains()接受2个参数(包括"this"指针),不能直接与QtConcurrent::filtered()一起使用,因为QtConcurrent::filtered()需要一个接受一个参数的函数。要使用QString::contains()和QtConcurrent::filtered(),我们必须为regexp参数提供一个值:
QStringList strings = ...; QFuture<QString> future = QtConcurrent::filtered(list, [](const QString &str) { return str.contains(QRegularExpression("^\\S+$")); // matches strings without whitespace })