好的,那我们现在开始今天的讲座,主题是 C++ folly::Future
与 folly::Promise
,这是 Facebook Folly 库中非常重要的异步编程利器。
大家好!今天我们要聊聊 folly::Future
和 folly::Promise
,这对黄金搭档,它们可以帮助你在 C++ 中轻松玩转异步编程。如果你之前被多线程搞得头昏脑胀,或者被回调地狱折磨得欲仙欲死,那么 folly::Future
和 folly::Promise
绝对是你的救星。
什么是 folly::Future
和 folly::Promise
?
简单来说,folly::Promise
负责“承诺”一个未来的值,而 folly::Future
则负责“等待”这个值的到来。它们就像一对情侣,Promise
负责制造惊喜,Future
负责满怀期待地等待。
folly::Promise
: 就像一个信使,它负责把结果从一个线程传递到另一个线程。你可以用Promise
来设置一个值,或者抛出一个异常。folly::Future
: 就像一个接收器,它负责等待Promise
传递过来的值。你可以从Future
中获取结果,或者处理异常。
为什么要用 folly::Future
和 folly::Promise
?
想象一下,你要从网络上下载一张图片,然后显示在界面上。如果你用同步的方式来做,那么在下载完成之前,你的界面就会卡住,用户体验非常糟糕。但如果你用异步的方式来做,就可以在后台下载图片,下载完成后再更新界面,这样就不会阻塞主线程了。
folly::Future
和 folly::Promise
可以让你更轻松地实现异步编程,它们提供了以下优点:
- 更简洁的代码: 相比于传统的多线程编程,
folly::Future
和folly::Promise
可以让你写出更简洁、更易读的代码。 - 更好的错误处理:
folly::Future
可以方便地处理异步操作中的异常,避免程序崩溃。 - 更强大的组合能力:
folly::Future
提供了丰富的操作符,可以让你轻松地组合多个异步操作。
folly::Promise
的用法
首先,我们来看看 folly::Promise
的基本用法。
#include <folly/Future.h>
#include <iostream>
#include <thread>
using namespace folly;
int main() {
// 创建一个 Promise 对象,类型是 int
Promise<int> promise;
// 获取与 Promise 关联的 Future 对象
Future<int> future = promise.getFuture();
// 在另一个线程中设置 Promise 的值
std::thread t([&]() {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
promise.setValue(42); // 设置 Promise 的值为 42
std::cout << "Promise set value to 42" << std::endl;
});
// 在主线程中等待 Future 的结果
try {
int result = future.get(); // 等待 Promise 设置值
std::cout << "Future got value: " << result << std::endl; // 输出:Future got value: 42
} catch (const std::exception& e) {
std::cerr << "Future got exception: " << e.what() << std::endl;
}
t.join();
return 0;
}
在这个例子中,我们创建了一个 Promise<int>
对象,并获取了与它关联的 Future<int>
对象。然后在另一个线程中,我们等待 2 秒钟,然后设置 Promise
的值为 42。在主线程中,我们调用 future.get()
来等待 Promise
设置值,并获取结果。
folly::Future
的用法
现在,我们来看看 folly::Future
的用法。
#include <folly/Future.h>
#include <iostream>
#include <thread>
using namespace folly;
Future<int> calculateAsync(int input) {
Promise<int> promise;
Future<int> future = promise.getFuture();
std::thread t([input, promise = std::move(promise)]() mutable {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
promise.setValue(input * 2);
std::cout << "CalculateAsync set value" << std::endl;
});
t.detach(); // 注意这里使用 detach,避免阻塞主线程
return future;
}
int main() {
Future<int> future = calculateAsync(21);
try {
int result = future.get();
std::cout << "Result: " << result << std::endl; // 输出:Result: 42
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待异步操作完成
return 0;
}
在这个例子中,我们定义了一个 calculateAsync
函数,它接受一个整数作为输入,并在另一个线程中计算它的两倍。这个函数返回一个 Future<int>
对象,表示异步计算的结果。在主线程中,我们调用 calculateAsync
函数,并等待 Future
的结果。
异常处理
folly::Future
提供了方便的异常处理机制。如果 Promise
设置了一个异常,那么 Future
在调用 get()
方法时会抛出这个异常。
#include <folly/Future.h>
#include <iostream>
#include <thread>
#include <stdexcept>
using namespace folly;
int main() {
Promise<int> promise;
Future<int> future = promise.getFuture();
std::thread t([&]() {
try {
throw std::runtime_error("Something went wrong!");
} catch (...) {
promise.setException(std::current_exception()); // 设置 Promise 的异常
std::cout << "Promise set exception" << std::endl;
}
});
try {
int result = future.get();
std::cout << "Result: " << result << std::endl;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl; // 输出:Exception: Something went wrong!
}
t.join();
return 0;
}
在这个例子中,我们在另一个线程中抛出了一个异常,并使用 promise.setException()
方法将异常设置到 Promise
中。在主线程中,我们调用 future.get()
方法,由于 Promise
设置了异常,所以 future.get()
方法会抛出这个异常。
Future
的组合操作
folly::Future
提供了丰富的操作符,可以让你轻松地组合多个异步操作。
then()
: 在Future
完成后执行一个函数。map()
: 将Future
的结果转换为另一种类型。flatMap()
: 将Future
的结果转换为另一个Future
。collectAll()
: 等待多个Future
完成,并返回一个包含所有结果的Future
。collectAny()
: 等待多个Future
中的任意一个完成,并返回这个Future
的结果。
下面是一些例子:
#include <folly/Future.h>
#include <iostream>
#include <vector>
using namespace folly;
Future<int> addOneAsync(int x) {
return makeFuture(x + 1); // 使用 makeFuture 快速创建一个 Future
}
int main() {
// then()
Future<int> future1 = makeFuture(10).then([](int x) {
std::cout << "Then: " << x << std::endl; // 输出:Then: 10
return x * 2;
});
std::cout << "Then Result: " << future1.get() << std::endl; // 输出:Then Result: 20
// map()
Future<std::string> future2 = makeFuture(20).map([](int x) {
return std::to_string(x);
});
std::cout << "Map Result: " << future2.get() << std::endl; // 输出:Map Result: 20
// flatMap()
Future<int> future3 = makeFuture(30).flatMap([](int x) {
return addOneAsync(x);
});
std::cout << "FlatMap Result: " << future3.get() << std::endl; // 输出:FlatMap Result: 31
// collectAll()
std::vector<Future<int>> futures;
futures.push_back(makeFuture(1));
futures.push_back(makeFuture(2));
futures.push_back(makeFuture(3));
Future<std::vector<int>> future4 = collectAll(futures);
std::vector<int> results = future4.get();
std::cout << "CollectAll Result: ";
for (int result : results) {
std::cout << result << " "; // 输出:CollectAll Result: 1 2 3
}
std::cout << std::endl;
return 0;
}
Future
和 Promise
的一些注意事项
Promise
只能设置一次值或异常。 如果你尝试多次设置值或异常,会抛出一个异常。Future
只能get()
一次。 如果你尝试多次get()
,会抛出一个异常。- 避免死锁。 在使用
Future
和Promise
时,要注意避免死锁。例如,不要在一个线程中等待同一个线程设置的Future
的结果。 detach
的使用。 在某些情况下,你需要使用std::thread::detach()
来避免阻塞主线程。但是,使用detach
需要小心,确保你的线程在程序结束之前完成。
与其他异步库的比较
folly::Future
和 folly::Promise
类似于其他语言中的 Promise
和 Future
,例如 JavaScript 中的 Promise
和 Java 中的 CompletableFuture
。它们都提供了类似的异步编程模型。
与其他 C++ 异步库相比,例如 std::future
和 std::promise
,folly::Future
提供了更多的功能和更好的性能。例如,folly::Future
提供了更多的组合操作,可以让你更方便地组合多个异步操作。
总结
folly::Future
和 folly::Promise
是 Facebook Folly 库中非常强大的异步编程工具。它们可以让你更轻松地编写异步代码,提高程序的性能和可维护性。
特性 | folly::Future 和 folly::Promise |
std::future 和 std::promise |
---|---|---|
功能 | 更多组合操作 (then, map, flatMap 等) | 较少组合操作 |
性能 | 通常更好 | 相对较差 |
易用性 | 更易于使用和理解 | 相对复杂 |
依赖 | 依赖于 Folly 库 | 标准库,无需额外依赖 |
希望今天的讲座能帮助你更好地理解 folly::Future
和 folly::Promise
,并在你的项目中应用它们。异步编程的世界非常广阔,希望大家多多探索,写出更高效、更稳定的代码!
最后,记住,folly::Future
和 folly::Promise
就像太极拳,看似缓慢,实则蕴含着强大的力量。掌握它们,你就能在异步编程的世界里游刃有余,成为真正的编程高手!