栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++20 协程

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++20 协程

1. 简介

协程就是一个可以挂起执行,稍后再恢复执行的函数。

协程是无栈式的(stackless),协程挂起时会返回到调用者或恢复者,且恢复执行所需的数据会分开存储,而不放在栈上。

只要一个函数包含 co_await、co_yield 或 co_return 关键字,则它就是协程。

一个协程关联有如下多个对象:

  • promise 对象:在协程内部操作此对象,协程通过它提交返回值或异常;
  • 协程句柄:在协程外部操作此对象,用于恢复协程的执行或销毁协程帧;
  • 协程状态:在堆上分配的内部状态。

2. co_await 操作符

co_await 会挂起当前协程,并返回到协程调用者或恢复者。

co_await expr;

其中,expr 需要是一个 awaiter。

co_await 包含的主要操作是:

  • 首先调用 awaiter.await_ready(),如果其返回值为 false,则挂起当前协程(填充协程状态);然后,
  • 调用 awaiter.await_suspend(handle),其中 handle 是一个协程句柄,如果此函数返回 void,则立即返回到协程的调用者或恢复者;如果 awaiter.await_suspend(handle) 抛出异常,则会捕获该异常,然后恢复协程,接着重新抛出该异常;
  • 其他函数可以通过 handle.resume() 来恢复协程,协程恢复之后会执行 awaiter.await_resume(),其返回值就是 co_await expr 的返回结果,接着继续执行协程体;
  • 当协程体再次遇到 co_await 或执行完毕时,它会返回到恢复者继续执行。

值得注意的是,可以将协程句柄传递到其他线程,然后在另一个线程中恢复协程的执行!


3. 协程执行流程

协程的大致执行流程如下:

  • 构造一个 promise 对象;
  • 调用 promise.get_return_object(),并在协程首次挂起时将调用结果返回到调用者;
  • 执行 co_await promise.initial_suspend();
  • 当 co_await promise.initial_suspend() 恢复时,开始执行协程体;
  • 当执行到协程体中的 co_return; 或协程体尾部时,调用 promise.return_void();
  • 当执行到协程体中的 co_return expr; 时,调用 promise.return_value(expr);
  • 如果协程以一个未捕获的异常终止时,会调用 promise.unhandled_exception();
  • 最后执行 co_await promise.final_suspend()。

当通过 co_return 或协程句柄来销毁协程状态时,它会执行如下动作:

  • 析构 promise 对象;
  • 析构协程参数的拷贝;
  • 释放协程状态所占内存;
  • 返回到协程调用者或恢复者。
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace std::literals;

using callback_t = std::function;

// 异步执行(模拟耗时的计算)
void asyncAdd(int v, callback_t cb) {
	thread t([v, cb]() {
		this_thread::sleep_for(5ms);
		int result = v + 100;
		cb(result);
	});

	t.detach();
}

// 协程的返回值类型
struct Task {
private:
	Task() {}
	
public:
	struct promise_type {
		Task get_return_object() {
			return Task();
		}

		suspend_never initial_suspend() {
			return suspend_never{};
		}

		suspend_never final_suspend() noexcept {
			return suspend_never{};
		}

		void return_void() {}

		void unhandled_exception() {
			terminate();
		}
	};
};

// co_await 操作数的类型
class AddAwaitable {
public:
	AddAwaitable(int initValue)
		: m_init(initValue), m_result(0) {}

	bool await_ready() {
		return false;
	}

	// 调用异步函数
	void await_suspend(std::coroutine_handle<> handle) {
		auto cb = [handle, this](int value) mutable {
			m_result = value;
			handle.resume();
			cout << "after resume.n";
		};

		asyncAdd(m_init, cb);
	}

	int await_resume() {
		return m_result;
	}

private:
	int m_init;
	int m_result;
};

Task addByCoroutine(int v) {
	cout << "tid1: " << this_thread::get_id() << 'n';

	int ret = co_await AddAwaitable(v);

	cout << "tid2: " << this_thread::get_id() << 'n';

	cout << "result = " << ret << 'n';
	co_return;
}

int main() {
	Task task(addByCoroutine(200));
	this_thread::sleep_for(1s);
	return 0;
}
tid1: 2896
tid2: 1140
result = 300
after resume.

其中,

struct suspend_never {
    constexpr bool await_ready() const noexcept {
        return true;
    }

    constexpr void await_suspend(coroutine_handle<>) const noexcept {}
    constexpr void await_resume() const noexcept {}
};

struct suspend_always {
    constexpr bool await_ready() const noexcept {
        return false;
    }

    constexpr void await_suspend(coroutine_handle<>) const noexcept {}
    constexpr void await_resume() const noexcept {}
};

4. co_yield 关键字
co_yield expr

等价于,

co_await promise.yield_value(expr)

例子:数字生成器

#include 
#include 

using namespace std;

struct Generator {
	struct promise_type;
	using handle_t = coroutine_handle;

	Generator(const Generator&) = delete;
	Generator& operator=(const Generator&) = delete;

	~Generator() {
		if (m_handle) {
			m_handle.destroy();
		}
	}

	struct promise_type {
		int value;

		auto get_return_object() {
			return Generator(handle_t::from_promise(*this));
		}

		auto initial_suspend() {
			return suspend_always();
		}

		auto final_suspend() noexcept {
			return suspend_always();
		}

		void return_void() {}

		auto yield_value(int v) {
			value = v;
			return suspend_always();
		}

		void unhandled_exception() {
			terminate();
		}
	};

	bool next() {
		if (m_handle) {
			m_handle.resume();
			return !m_handle.done();
		}

		return false;
	}

	int value() const {
		return m_handle.promise().value;
	}

private:
	Generator(handle_t h)
		: m_handle(h) {}

private:
	handle_t m_handle;
};

Generator f(int n) {
	int value = 1;
	
	while (value <= n) {
		co_yield value++;
	}
}


int main() {
	Generator g(f(10));

	while (g.next()) {
		cout << g.value() << ' ';
	}
	cout << 'n';
}
1 2 3 4 5 6 7 8 9 10
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/691975.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号