c++20协程实现await及suspend效果

发布时间 2023-04-15 15:59:29作者: Netsharp

例子是掌握c++20协程的的起点,网上很多的协程资料说的很复杂,但是例子举得都太差劲了,我这里写了一个可以await与suspend的列子。

1. 协程的基本框架

// suspend.cpp

#include <chrono>
#include <coroutine>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>

struct suspend {
  struct promise_type {
    promise_type() {}
    suspend get_return_object() {
      return {std::coroutine_handle<suspend::promise_type>::from_promise(*this)};
    }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
    void return_void() {}
    void unhandled_exception() {}
  };

  typedef std::coroutine_handle<suspend::promise_type> suspendHandler;

  suspendHandler handle_;  // 这个变量名可以随便起
};

typedef std::function<void(suspend::suspendHandler, int *)> AwaiterFunction;

template <typename T>
struct await {
  AwaiterFunction fun;
  std::shared_ptr<T> ptr = std::make_shared<T>();

  await(AwaiterFunction fun) : fun(fun){};
  bool await_ready() { return false; }
  void await_suspend(suspend::suspendHandler handle) {
    T *p = ptr.get();
    fun(handle, p);
  }
  std::shared_ptr<int> await_resume() { return ptr; }
};

2. 测试方法

// suspend.cpp

await<int> invokeService() {
  auto wait = await<int>([](suspend::suspendHandler handle, int *p) {
    // *p = 2025;
    // handle.resume();

    int *ptr = p;
    std::thread([handle, ptr]() mutable {
      *ptr = 2023;
      std::cout << "sleep 2 seconds \n";
      std::this_thread::sleep_for(std::chrono::seconds(2));
      handle.resume();  // 可以简写为 handle();
    }).detach();
  });
  return wait;
}

suspend test() {
  auto rc = co_await invokeService();

  std::cout << "rc = " << rc << std::endl;
  std::cout << "3 thread id=" << std::this_thread::get_id() << "\n";
}

3. main函数测试

// suspend.cpp

int main() {
  std::cout << "1 thread id=" << std::this_thread::get_id() << "\n";

  test();

  std::cout << "2 thread id=" << std::this_thread::get_id() << "\n";
  std::this_thread::sleep_for(std::chrono::seconds(4));

  return 0;
}

4. makefile

app : suspend.cpp
	rm -rf ./app
	g++ -o $@ $^ -std=c++2a

5. 执行结果

# ./app
1 thread id=140041734599616
2 thread id=140041734599616
sleep 2 seconds 
rc = 0x560c1bdde350
3 thread id=140041734583872