【翻译】Seastar 迷你教程
教程翻译自Seastar官方文档:https://github.com/scylladb/seastar/blob/master/doc/mini-tutorial.md
转载请注明出处:https://www.cnblogs.com/morningli/p/15920456.html
futures 和 promises
future 是现在还不可用的计算的结构,比如:
- 正在从网络读取的数据缓冲区(data buffer)
- 定时器到期的事件
- 完成写磁盘的操作
- 其他未来的值的计算结果
promise是一个带有future的对象或函数,并期望它会填充future。
promise 和 future 简化了异步编程,因为它们解耦了事件生产者(promise)和事件消费者(使用future的逻辑)。无论promise是在future填充之前还是之后被消费,都不会对代码的结果有影响。
消费 future
您通过使用其then()方法来使用future,并为其提供回调(通常是lambda)。例如,考虑以下操作:
future<int> get(); // 承诺最终将产生一个 int
future<> put(int) // 承诺存储一个 int
void f() {
get().then([] (int value) {
put(value + 1).then([] {
std::cout << "value stored successfully\n";
});
});
}
在这里,我们启动一个get()操作,请求当它完成时,一个 put()操作将被安排一个递增的值。我们还要求当put()完成时,一些文本会被打印出来。
链式future
如果then() lambda 返回一个future(称为 x),那么then() 将返回一个将接收相同值的future(称为 y)。这消除了嵌套 lambda 块的需要;例如上面的代码可以重写为:
future<int> get(); // 承诺最终将产生一个 int
future<> put(int) // 承诺存储一个 int
void f() {
get().then([] (int value) {
return put(value + 1);
}).then([] {
std::cout << "value stored successfully\n";
});
}
循环
循环是通过尾调用实现的;例如:
future<int> get(); // 承诺最终将产生一个 int
future<> put(int) // 承诺存储一个 int
future<> loop_to(int end) {
if (value == end) {
return make_ready_future<>();
}
get().then([end] (int value) {
return put(value + 1);
}).then([end] {
return loop_to(end);
});
}
make_ready_future ()函数返回一个已经可用的future —— 对应于循环终止条件,不需要进一步的I/O。
揭秘
当上面的循环运行时,两个then方法调用都会立即执行 —— 但不执行主体。会发生以下情况:
get()被调用,启动 I/O 操作,并分配一个临时结构(称为f1)。- 第一个
then()调用将其主体链接到f1并分配另一个临时结构f2. - 第二个
then()调用将其主体链接到f2. - 同样,所有这些都立即运行,无需等待任何东西。
在完成发起的 I/O 操作get()后,它会调用存储在f1中的 continuation,调用它并释放f1。continuation调用put(),它启动执行存储所需的 I/O 操作,并分配一个临时对象f12,并将一些粘合代码链接到它。
由完成发起的 I/O 操作put()后,它调用与f12关联的continuation,只是告诉它调用与关联的continuation f2。这个continuation只是调用 loop_to(). f12和f2都被释放。loop_to()然后调用 get(),这将重新开始该过程,分配新版本f1和f2。

处理异常
如果.then()子句抛出异常,调度程序将捕获它并取消任何依赖.then()子句。如果要捕获异常,请.then_wrapped()在末尾添加一个子句:
future<buffer> receive();
request parse(buffer buf);
future<response> process(request req);
future<> send(response resp);
void f() {
receive().then([] (buffer buf) {
return process(parse(std::move(buf));
}).then([] (response resp) {
return send(std::move(resp));
}).then([] {
f();
}).then_wrapped([] (auto&& f) {
try {
f.get();
} catch (std::exception& e) {
// 你的处理程序放在这里
}
});
}
以前的future作为参数传递给 lambda,它的值可以用f.get()查询. 当get()变量作为函数调用时,它将重新引发中止处理的异常,然后您可以应用任何需要的错误处理。它本质上是下面代码的一种转变:
buffer receive();
request parse(buffer buf);
response process(request req);
void send(response resp);
void f() {
try {
while (true) {
auto req = parse(receive());
auto resp = process(std::move(req));
send(std::move(resp));
}
} catch (std::exception& e) {
// 你的处理程序放在这里
}
}
但是请注意,无论.then_wrapped()是否发生异常,都会安排该子句。因此,仅仅.then_wrapped()执行的事实并不意味着抛出了异常。只有执行 catch 块才能保证这一点。
如下所示:
future<my_type> receive();
void f() {
receive().then_wrapped([] (future<my_type> f) {
try {
my_type x = f.get();
return do_something(x);
} catch (std::exception& e) {
// 你的处理程序放在这里
}
});
}
设置说明
SeaStar 是一个高性能框架,默认情况下经过调整以获得最佳性能。因此,我们倾向于轮询与中断驱动。我们的假设是为 SeaStar 编写的应用程序将忙于处理 100,000 IOPS 及以上。轮询意味着我们的每个核心都将消耗 100% 的 cpu,即使没有给它任何工作。
本文来自博客园,作者:morningli,转载请注明原文链接:https://www.cnblogs.com/morningli/p/15920456.html

浙公网安备 33010602011771号