考虑以下来自N3650的示例:
int cnt = 0;
do {
cnt = await streamR.read(512, buf);
if (cnt == 0)
break;
cnt = await streamW.write(cnt, buf);
} while (cnt > 0);
我可能遗漏了一些东西,但是如果我理解async
和await
那么,当效果等同于写作时,用上面的例子展示两个结构的用处有什么意义:
int cnt = 0;
do {
cnt = streamR.read(512, buf).get();
if (cnt == 0)
break;
cnt = streamW.write(cnt, buf).get();
} while (cnt > 0);
read().get()
和write().get()
调用是同步的吗?
答案 0 :(得分:7)
await关键字不等于将来调用get。您可能会更喜欢这样看,假设您从这开始:
future<T> complex_function()
{
do_some_stuff();
future<Result> x = await some_async_operation();
return do_some_other_stuff(x);
}
这在功能上与
大致相同future<T> complex_function()
{
do_some_stuff();
return some_async_operation().then([=](future<Result> x) {
return do_some_other_stuff(x);
});
}
或多或少注意,因为存在一些资源管理问题,不应将do_some_stuff
中创建的变量复制到执行do_some_other_stuff
,就像lambda版本一样。
第二个变体使得在调用时会发生什么变得更清楚。
do_some_stuff()
时,complex_function
会同步调用。some_async_operation
被异步调用并导致未来。执行此操作的确切时刻取决于您的实际异步调用实现,它可能在您使用线程时立即执行,可能是在您使用deferred执行时调用.get()
时。 do_some_other_stuff
,而是将其链接到步骤2中获得的未来。这意味着只要some_async_operation
的结果准备就绪,就可以执行它,但之前不会执行。除此之外,它的执行时刻由运行时决定。如果实现只包装then
提议,这意味着它将继承父未来的执行者/启动策略(根据N3558)。答案 1 :(得分:6)
一个更完整的例子(希望是正确的):
future<void> forwardMsgs(istream& streamR, ostream& streamW) async
{
char buf[512];
int cnt = 0;
do {
cnt = await streamR.read(512, buf);
if (cnt == 0)
break;
cnt = await streamW.write(cnt, buf);
} while (cnt > 0);
}
future<void> fut = forwardMsgs(myStreamR, myStreamW);
/* do something */
fut.get();
重要的一点是(引用草案):
暂停后,可恢复的函数可以由运行时的调度逻辑恢复,并最终完成其逻辑,此时它执行return语句(显式或隐式)并在占位符中设置函数的结果值。 / p>
和
可恢复功能可以在暂停执行后继续执行另一个线程。
也就是说,最初调用forwardMsgs
的线程可以在任何暂停点返回。如果是这样,在/* do something */
行期间,forwardMsgs
中的代码可以由另一个线程执行,即使该函数已被“同步”调用”
此示例与
非常相似future<void> fut = std::async(forwardMsgs, myStreamR, myStreamW);
/* do something */
fut.get();
区别在于可恢复功能可以由不同的线程执行:不同的线程可以在每个恢复/暂停点之后恢复执行(可恢复功能)。
答案 2 :(得分:3)
我认为我的想法是streamR.read()
和streamW.write()
调用是异步I / O操作并返回期货,await
表达式会自动等待它们。
因此,等效的同步版本必须调用future::get()
才能获得结果,例如
int cnt = 0;
do {
cnt = streamR.read(512, buf).get();
if (cnt == 0)
break;
cnt = streamW.write(cnt, buf).get();
} while (cnt > 0);
你指出这里没有并发性是正确的。但是,在可恢复功能的上下文中,await
使行为与上面的代码段不同。当达到await
时,函数将返回future
,因此即使在等待其他结果时await
阻止了可恢复功能,函数的调用者仍可继续进行而不会阻塞(例如,在这种情况下,read()
或write()
调用完成。)可恢复的函数可能会异步恢复运行,因此当调用者正在执行其他操作时,结果将在后台运行。
答案 3 :(得分:2)
这是示例函数的正确转换,不使用await:
struct Copy$StackFrame {
promise<void> $result;
input_stream& streamR;
output_stream& streamW;
int cnt;
char buf[512];
};
using Copy$StackPtr = std::shared_ptr<Copy$StackFrame>;
future<void> Copy(input_stream& streamR, output_stream& streamW) {
Copy$StackPtr $stack{ new Copy$StackFrame{ {}, streamR, streamW, 0 } };
future<int> f$1 = $stack->streamR.read(512, stack->buf);
f$1.then([$stack](future<int> f) { Copy$Cont1($stack, std::move(f)); });
return $stack->$result.get_future();
}
void Copy$Cont1(Copy$StackPtr $stack, future<int> f$1) {
try {
$stack->cnt = f$1.get();
if ($stack->cnt == 0) {
// break;
$stack->$result.set_value();
return;
}
future<int> f$2 = $stack->streamW.write($stack->cnt, $stack->buf);
f$2.then([$stack](future<int> f) { Copy$Cont2($stack, std::move(f)); });
} catch (...) {
$stack->$result.set_exception(std::current_exception());
}
}
void Copy$Cont2(Copy$StackPtr $stack, future<int> f$2) {
try {
$stack->cnt = f$2.get();
// while (cnt > 0)
if (cnt <= 0) {
$stack->$result.set_value();
return;
}
future<int> f$1 = $stack->streamR.read(512, stack->buf);
f$1.then([$stack](future<int> f) { Copy$Cont1($stack, std::move(f)); });
} catch (...) {
$stack->$result.set_exception(std::current_exception());
}
}
正如您所看到的,这里的编译器转换非常复杂。这里的关键点是,与get()
版本不同,原始Copy
会在第一次异步调用完成后立即返回其未来。
答案 4 :(得分:0)
我对这两个代码示例之间差异的含义有同样的问题。让我们重新写一点,以便更完整。
// Having two functions
future<void> f (istream&streamR, ostream&streamW) async
{ int cnt = 0;
do {
cnt = await streamR.read(512, buf);
if (cnt == 0)
break;
cnt = await streamW.write(cnt, buf);
} while (cnt > 0);
}
void g(istream&streamR, ostream&streamW)
{ int cnt = 0;
do {
cnt = streamR.read(512, buf).get();
if (cnt == 0)
break;
cnt = streamW.write(cnt, buf).get();
} while (cnt > 0);
}
// what is the difference between
auto a = f(streamR, streamW);
// and
auto b = async(g, streamR, streamW);
你仍然需要至少三个筹码。在这两种情况下,主线程都不会被阻止。是否假设await将比未来&lt;&gt;:get()?更有效地实现编译器。那么,没有等待的那个现在可以使用了。
由于 亚当·齐林斯基