Skip to content

本文档用于比较协程与传统同步写法与 Future/Promise 写法的异同。本文档使用的代码可在 async_simple/demo_example/ReadFiles 中找到。

ReadFiles 是一个计算多个文件中特定字符出现次数的程序。使用了三种写法实现这一功能:

  • 传统同步写法。
  • 基于 Future/Promise 的异步写法。
  • 使用协程(Lazy)的写法。

同步写法

cpp
uint64_t CountCharInFiles(const std::vector<FileName> &Files, char c) {
    uint64_t ReadSize = 0;
    for (const auto &File : Files)
        ReadSize += CountCharInFile(File, c);
    return ReadSize;
}

同步的写法非常直观,只需要依次遍历一遍即可了。

异步写法

虽然同步的写法非常简单直观,但当 File 数量很多,File 平均内容也很多,甚至 File 存储于远端时,程序员会考虑使用异步写法来提升效率。

cpp
Future<uint64_t> CountCharInFilesAsync(const std::vector<FileName> &Files, char c) {
    // std::shared_ptr is necessary here. Since ReadSize may be used after
    // CountCharInFilesAsync function ends.
    auto ReadSize = std::make_shared<uint64_t>(0);
    // We need to introduce another API `do_for_each` here.
    return do_for_each(std::move(Files), [ReadSize, c](auto &&File){
        return CountCharInFileAsyncImpl(File, c).thenValue([ReadSize](auto &&Size) {
            *ReadSize += Size;
            return makeReadyFuture(Unit());
        });
    }).thenTry([ReadSize] (auto&&) { return makeReadyFuture<uint64_t>(*ReadSize); });;
}

上面的例子是该功能对应的异步版本。可以看到,我们不但需要引入 std::shared_ptr 与新的 API do_for_each,同时代码结构也复杂了非常多。

协程写法

通过协程,程序员可以用同步写法写异步程序。兼具开发效率与运行效率。

cpp
Lazy<uint64_t> CountCharInFilesCoro(const std::vector<FileName> &Files, char c) {
    uint64_t ReadSize = 0;
    for (const auto &File : Files)
        ReadSize += co_await CountCharFileCoroImpl(File, c);
    co_return ReadSize;
}

我们可以看到,使用协程的写法与基于同步的写法基本一致,简单直观。而在运行效率上,协程版本也会好于异步版本。一方面协程版本不需要 std::shared_ptr,另一方面相比于异步版本,协程版本的代码更少且更连续,会更方便编译器做优化。

This website is released under the MIT License.