Last active
April 20, 2017 05:11
-
-
Save benlau/cb1cc2adae75aa8a9590315e0f5a66cd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example code in the article of "Multithreaded Programming with Future & Promise" | |
// http://blog.qt.io/blog/2017/04/18/multithreaded-programming-future-promise/#comments | |
/* Step 1 - An ImageReader class */ | |
class ImageReader : public QObject { | |
public: | |
QFuture<QImage> read(const QString& fileName); | |
}; | |
QFuture<QImage> ImageReader::read(const QString &fileName) | |
{ | |
auto readImageWorker = [](const QString &fileName) { | |
QImage image; | |
image.load(fileName); | |
return image; | |
}; | |
return QtConcurrent::run(readImageWorker, fileName); | |
} | |
// Example of use | |
{ | |
ImageReader reader; | |
QFuture<QImage> future = reader.read(INPUT); | |
QFutureWatcher<QImage> *watcher = new QFutureWatcher<QImage>(); | |
QObject::connect(watcher, &QFutureWatcher<QImage>::finished, | |
[=]() { | |
setImage(future.result()); | |
}); | |
watcher->setFuture(future); | |
} | |
/* Step 2 - Image Caching (without AsyncFuture) */ | |
class ImageReader : public QObject { | |
public: | |
bool isCached(const QString& fileName) const; | |
QImage readCache(const QString& fileName) const; | |
QFuture<QImage> read(const QString& fileName); | |
private: | |
QMap<QString,QImage> m_cache; | |
}; | |
QFuture<QImage> ImageReader::read(const QString &fileName) | |
{ | |
auto readImageWorker = [](const QString &fileName) { | |
QImage image; | |
image.load(fileName); | |
return image; | |
}; | |
QFuture<QImage> future = QtConcurrent::run(readImageWorker, fileName); | |
QFutureWatcher<QImage> *watcher = new QFutureWatcher<QImage>(this); | |
auto updateCache = [=]() { | |
m_cache[fileName] = future.result(); | |
watcher->deleteLater(); | |
}; | |
connect(watcher, &QFutureWatcher<QImage>::finished, updateCache); | |
watcher->setFuture(future); | |
return future; | |
} | |
bool ImageReader::isCached(const QString &fileName) const | |
{ | |
return m_cache.contains(fileName); | |
} | |
QImage ImageReader::readCache(const QString &fileName) const | |
{ | |
return m_cache[fileName]; | |
} | |
/* Step 3 - Image Caching with AsyncFuture */ | |
class ImageReader : public QObject { | |
public: | |
QFuture<QImage> read(const QString& fileName); | |
private: | |
QMap<QString,QImage> m_cache; | |
QMap<QString, QFuture<QImage>> m_futures; | |
}; | |
QFuture<QImage> ImageReader::read(const QString &fileName) | |
{ | |
if (m_cache.contains(fileName)) { | |
// Cache hit. Return an already finished QFuture object with the image | |
auto defer = AsyncFuture::deferred<QImage>(); | |
defer.complete(m_cache[fileName]); | |
return defer.future(); | |
} | |
if (m_futures.contains(fileName)) { | |
// It is loading. Return the running QFuture | |
return m_futures[fileName]; | |
} | |
auto readImageWorker = [](const QString &fileName) { | |
QImage image; | |
image.load(fileName); | |
return image; | |
}; | |
auto updateCache = [=](QImage result) { | |
m_cache[fileName] = result; | |
m_futures.remove(fileName); | |
return result; | |
}; | |
QFuture<QImage> future = AsyncFuture::observe(QtConcurrent::run(readImageWorker, fileName)).subscribe(updateCache).future(); | |
m_futures[fileName] = future; | |
return future; | |
} | |
/* Step 4 - ImageReader with readScaled function */ | |
class ImageReader : public QObject { | |
public: | |
QFuture<QImage> read(const QString& fileName); | |
QFuture<QImage> readScaled(const QString& fileName, const QSize& size); | |
private: | |
QMap<QString,QImage> m_cache; | |
QMap<QString, QFuture<QImage>> m_futures; | |
}; | |
QFuture<QImage> ImageReader::read(const QString &fileName) | |
{ | |
if (m_cache.contains(fileName)) { | |
// Cache hit. Return an already finished QFuture object with the image | |
auto defer = AsyncFuture::deferred<QImage>(); | |
defer.complete(m_cache[fileName]); | |
return defer.future(); | |
} | |
if (m_futures.contains(fileName)) { | |
// It is loading. Return the running QFuture | |
return m_futures[fileName]; | |
} | |
auto readImageWorker = [](const QString &fileName) { | |
QImage image; | |
image.load(fileName); | |
return image; | |
}; | |
auto updateCache = [=](QImage result) { | |
m_cache[fileName] = result; | |
m_futures.remove(fileName); | |
return result; | |
}; | |
QFuture<QImage> future = AsyncFuture::observe(QtConcurrent::run(readImageWorker, fileName)).subscribe(updateCache).future(); | |
m_futures[fileName] = future; | |
return future; | |
} | |
QFuture<QImage> ImageReader::readScaled(const QString &fileName, const QSize &size) | |
{ | |
auto scaleImageWorker = [=](QImage input) { | |
// Run in worker thread | |
return input.scaled(size); | |
}; | |
auto callback = [=](QImage result) { | |
// Run in main thread | |
return QtConcurrent::run(scaleImageWorker, result); | |
}; | |
QFuture<QImage> input = read(fileName); | |
QFuture<QImage> output = AsyncFuture::observe(input).subscribe(callback).future(); | |
return output; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment