C++ `folly::Future` 与 `folly::Promise`:Facebook 异步库的深度解析

好的,那我们现在开始今天的讲座,主题是 C++ folly::Futurefolly::Promise,这是 Facebook Folly 库中非常重要的异步编程利器。

大家好!今天我们要聊聊 folly::Futurefolly::Promise,这对黄金搭档,它们可以帮助你在 C++ 中轻松玩转异步编程。如果你之前被多线程搞得头昏脑胀,或者被回调地狱折磨得欲仙欲死,那么 folly::Futurefolly::Promise 绝对是你的救星。

什么是 folly::Futurefolly::Promise

简单来说,folly::Promise 负责“承诺”一个未来的值,而 folly::Future 则负责“等待”这个值的到来。它们就像一对情侣,Promise 负责制造惊喜,Future 负责满怀期待地等待。

  • folly::Promise 就像一个信使,它负责把结果从一个线程传递到另一个线程。你可以用 Promise 来设置一个值,或者抛出一个异常。
  • folly::Future 就像一个接收器,它负责等待 Promise 传递过来的值。你可以从 Future 中获取结果,或者处理异常。

为什么要用 folly::Futurefolly::Promise

想象一下,你要从网络上下载一张图片,然后显示在界面上。如果你用同步的方式来做,那么在下载完成之前,你的界面就会卡住,用户体验非常糟糕。但如果你用异步的方式来做,就可以在后台下载图片,下载完成后再更新界面,这样就不会阻塞主线程了。

folly::Futurefolly::Promise 可以让你更轻松地实现异步编程,它们提供了以下优点:

  • 更简洁的代码: 相比于传统的多线程编程,folly::Futurefolly::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;
}

FuturePromise 的一些注意事项

  • Promise 只能设置一次值或异常。 如果你尝试多次设置值或异常,会抛出一个异常。
  • Future 只能 get() 一次。 如果你尝试多次 get(),会抛出一个异常。
  • 避免死锁。 在使用 FuturePromise 时,要注意避免死锁。例如,不要在一个线程中等待同一个线程设置的 Future 的结果。
  • detach 的使用。 在某些情况下,你需要使用 std::thread::detach() 来避免阻塞主线程。但是,使用 detach 需要小心,确保你的线程在程序结束之前完成。

与其他异步库的比较

folly::Futurefolly::Promise 类似于其他语言中的 PromiseFuture,例如 JavaScript 中的 Promise 和 Java 中的 CompletableFuture。它们都提供了类似的异步编程模型。

与其他 C++ 异步库相比,例如 std::futurestd::promisefolly::Future 提供了更多的功能和更好的性能。例如,folly::Future 提供了更多的组合操作,可以让你更方便地组合多个异步操作。

总结

folly::Futurefolly::Promise 是 Facebook Folly 库中非常强大的异步编程工具。它们可以让你更轻松地编写异步代码,提高程序的性能和可维护性。

特性 folly::Futurefolly::Promise std::futurestd::promise
功能 更多组合操作 (then, map, flatMap 等) 较少组合操作
性能 通常更好 相对较差
易用性 更易于使用和理解 相对复杂
依赖 依赖于 Folly 库 标准库,无需额外依赖

希望今天的讲座能帮助你更好地理解 folly::Futurefolly::Promise,并在你的项目中应用它们。异步编程的世界非常广阔,希望大家多多探索,写出更高效、更稳定的代码!

最后,记住,folly::Futurefolly::Promise 就像太极拳,看似缓慢,实则蕴含着强大的力量。掌握它们,你就能在异步编程的世界里游刃有余,成为真正的编程高手!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注