【翻译】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