我知道了从< code>std::async返回的< code>future具有某种特殊共享状态的原因,通过这种状态,< code >等待返回的future发生在future的析构函数中。但是当我们使用< code>std::pakaged_task时,它的未来不会表现出同样的行为。要完成打包的任务,必须从< code>packaged_task显式调用< code>future对象上的< code>get()。
现在我的问题是:
std::async
vsstd::packaged_task
)?std::packaged_task
返回的未来
?或者,换句话说,如何停止std::packaged_task
未来
的相同行为?要查看上下文,请参阅下面的代码:
它不会等待完成倒计时
任务。但是,如果我取消注释 // int value = ret.get();
,它将完成倒计时
,这是显而易见的,因为我们实际上是在阻止返回的未来。
// packaged_task example
#include <iostream> // std::cout
#include <future> // std::packaged_task, std::future
#include <chrono> // std::chrono::seconds
#include <thread> // std::thread, std::this_thread::sleep_for
// count down taking a second for each value:
int countdown (int from, int to) {
for (int i=from; i!=to; --i) {
std::cout << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Lift off!" <<std::endl;
return from-to;
}
int main ()
{
std::cout << "Start " << std::endl;
std::packaged_task<int(int,int)> tsk (countdown); // set up packaged_task
std::future<int> ret = tsk.get_future(); // get future
std::thread th (std::move(tsk),10,0); // spawn thread to count down from 10 to 0
// int value = ret.get(); // wait for the task to finish and get result
std::cout << "The countdown lasted for " << std::endl;//<< value << " seconds.\n";
th.detach();
return 0;
}
如果我使用std::async在另一个
线程上执行任务倒计时
,无论我是否在返回的未来
对象上使用get(),
它总是会完成任务。
// packaged_task example
#include <iostream> // std::cout
#include <future> // std::packaged_task, std::future
#include <chrono> // std::chrono::seconds
#include <thread> // std::thread, std::this_thread::sleep_for
// count down taking a second for each value:
int countdown (int from, int to) {
for (int i=from; i!=to; --i) {
std::cout << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Lift off!" <<std::endl;
return from-to;
}
int main ()
{
std::cout << "Start " << std::endl;
std::packaged_task<int(int,int)> tsk (countdown); // set up packaged_task
std::future<int> ret = tsk.get_future(); // get future
auto fut = std::async(std::move(tsk), 10, 0);
// int value = fut.get(); // wait for the task to finish and get result
std::cout << "The countdown lasted for " << std::endl;//<< value << " seconds.\n";
return 0;
}
行为的变化是由于<code>std::thread</code>和<code>std::async</code>之间的差异造成的。
在第一个示例中,您已经通过分离创建了一个守护线程。在这里打印< code>std::cout
在第二个示例中,您使用std::启动::deferred
策略启动线程函数。std::a同步的行为是:
如果选择异步策略,相关的线程完成将与等待共享状态的第一个函数的成功返回同步,或者与释放共享状态的最后一个函数的返回同步,无论哪一个先出现。
在此示例中,对于同一个共享状态,您有两个未来。在退出main时调用它们的dtor之前,异步任务必须完成。即使您没有明确定义任何未来,创建和销毁的临时未来(从调用std::async
返回)将意味着任务在主线程退出之前完成。
这是Scott Meyers的一篇很棒的博文,阐明了< code>std::future的行为
相关SO帖子。
@尼古拉·博拉斯已经非常满意地回答了这个问题。因此,我将尝试从不同的角度回答这个问题,详细阐述@Nicol Bolas已经提到的要点。
考虑这个我们想以各种方式执行的简单函数:
int add(int a, int b) {
std::cout << "adding: " << a << ", "<< b << std::endl;
return a + b;
}
暂时忘掉std::packaged_task
、std::future
和std::async
,让我们后退一步,重新审视一下std::函数
的工作方式以及它会导致什么问题。
std::function<int(int,int)> f { add };
一旦我们有了f
,我们就可以在同一个线程中执行它,如:
int result = f(1, 2); //note we can get the result here
或者,在另一个线程中,如下所示:
std::thread t { std::move(f), 3, 4 };
t.join();
如果我们仔细观察,我们会意识到在不同的线程中执行f
会产生一个新问题:我们如何获得函数的结果?在同一线程中执行f
没有这个问题 - 我们得到结果作为返回值,但是当在不同的线程中执行它时,我们没有任何方法可以获得结果。这正是 std::p ackaged_task
所解决的问题。
特别是,它在线程之间创建一个通道,将结果发送给另一个线程。除此之外,它或多或少与< code>std::function相同。
std::packaged_task<int(int,int)> f { add }; // almost same as before
std::future<int> channel = f.get_future(); // get the channel
std::thread t{ std::move(f), 30, 40 }; // same as before
t.join(); // same as before
int result = channel.get(); // problem solved: get the result from the channel
现在您看到了std::packaged_task
如何解决由std::function
创建的问题。然而,这并不意味着std::packaged_task
必须在不同的线程中执行。您也可以在同一个线程中执行它,就像std::function
一样,尽管您仍然会从通道中获得结果。
std::packaged_task<int(int,int)> f { add }; // same as before
std::future<int> channel = f.get_future(); // same as before
f(10, 20); // execute it in the current thread !!
int result = channel.get(); // same as before
因此,从根本上讲,std::function
和std::packaged_task
,都是类似的东西:它们只是包装可调用的实体,但有一个区别:std::packated_tash
是多线程友好的,因为它提供了一个通道,可以将结果传递给其他线程。它们都不会自己执行包装的可调用实体。需要在同一个线程或另一个线程中调用它们,以执行包装的可调用实体。所以在这个空间里基本上有两种东西:
std::function
,std::packaged_task
等。这是一件不同的事情,因为它结合了执行的内容和执行的方式/地点。
std::future<int> fut = std::async(add, 100, 200);
int result = fut.get();
请注意,在这种情况下,创建的未来有一个关联的执行者,这意味着未来将在某个时候完成,因为有人在幕后执行事情。但是,如果std::packaged_task
创建了未来,则不一定有一个执行者,如果创建的任务从未交给任何执行者,则该未来可能永远不会完成。
希望这能帮助你了解幕后的情况。请参阅在线演示。
好吧,在这一点上,很明显可以创建两种std::future
:
std::async
创建。这样的未来有一个相关的执行者,因此可以完成。std::p ackaged_task
或类似的东西来创建。这种未来不一定有相关的遗嘱执行人,因此可能会或可能不会完成。因为,在第二种情况下,未来不一定有关联的执行程序,所以它的析构函数不是为它的完成/等待而设计的,因为它可能永远不会完成:
{
std::packaged_task<int(int,int)> f { add };
std::future<int> fut = f.get_future();
} // fut goes out of scope, but there is no point
// in waiting in its destructor, as it cannot complete
// because as `f` is not given to any executor.
希望这个答案能帮助你从不同的角度理解事情。
std::async
对如何以及在何处执行任务有明确的了解。这就是它的工作:执行任务。要做到这一点,它必须把它放在某个地方。某个地方可能是html" target="_blank">线程池,新创建的线程,或者在破坏未来的
任何人执行的地方。
因为async
知道函数将如何执行,所以它拥有100%的信息来构建一个机制,该机制可以在潜在的异步执行结束时进行通信,并确保如果您销毁未来的
,那么将执行该函数的任何机制最终都会着手实际执行它。毕竟,它知道该机制是什么。
但是packaged_task
没有。packaged_task
所做的只是存储一个可以使用给定参数调用的可调用对象,创建一个带有函数返回值类型的Promise
,并提供一种方法来获取未来
和执行生成值的函数。
任务实际执行的时间和地点与< code>packaged_task无关。如果没有这方面的知识,使< code>future的析构函数与任务同步所需的同步就无法构建。
假设您要在新创建的线程上执行任务。好了,为了将其执行与未来的
破坏同步,您需要一个互斥体,析构函数将阻止该互斥体,直到任务线程完成。
但是,如果要在与 future
析构函数的调用方相同的线程中执行任务,该怎么办?好吧,那么您不能使用互斥体来同步它,因为它们都在同一线程上。相反,您需要让析构函数调用任务。这是一个完全不同的机制,它取决于你计划如何执行。
因为< code>packaged_task不知道您打算如何执行它,所以它不能做任何事情。
请注意,这不是< code>packaged_task所独有的。从用户创建的< code>promise对象创建的所有< code>future将不具有< code>async的< code>future的特殊属性
因此,问题确实应该是为什么异步
以这种方式工作,而不是为什么其他人不这样做。
如果你想知道这一点,那是因为两个相互竞争的需求:异步
需要是一种高级的,脑死亡的简单方法来获得异步执行(对于这种方式,销毁时同步是有意义的),除了其析构函数的行为之外,没有人想要创建一个与现有类型相同的新未来
类型。因此,他们决定超载未来
的工作方式,使其实现和使用复杂化。
并行开发挺复杂的,特别是在试图用好线程和锁的过程中。如果要用到条件变量或std-atomics(一种无锁开发方式),那就更复杂了。C++0x提供了future和promise来简化任务线程间的返回值操作;同时为启动任务线程提供了packaged_task以方便操作。其中的关键点是允许2个任务间使用无(显式)锁的方式进行值传递;标准库帮你高效的做好这些了。基本思路很简单:当一个任务需要向父线程(启动
问题内容: 我尝试将matlab代码转换为numpy,并发现numpy与std函数的结果不同。 在matlab中 在numpy中 这正常吗?我应该如何处理呢? 问题答案: NumPy函数采用一个可选参数:“自由度增量”。默认情况下是。对其进行设置以获取MATLAB结果: 要添加更多上下文,在计算方差(标准偏差为平方根)时,通常将其除以我们拥有的值的数量。 但是,如果我们从较大的分布中选择元素的随机
https://godbolt.org/z/P97MaK 我玩的概念和预期d::is_equality_comparable工作矢量,但它没有。 编译错误在 内部失败,而不是在受概念保护的函数边界处失败。 这是错误还是预期行为?
在过去的几个月里,我一直在学习C语言并使用终端。我的代码使用g和c11编译并运行得很好,但在过去几天里它开始出现错误,此后我在编译时遇到了问题。我唯一可以编译和运行的程序依赖于旧的C标准。 我第一次遇到的错误包括 尝试使用ecg$g-o stoi_试验stoi_试验编译。cpp-std=c 11 大堆cpp:13:22:错误:命名空间“std”中没有名为“stoi”的成员;你是说“阿托伊”吗?in
标准库函数bind()和function()定义于头文件<functional>中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。bind()接受一个函数(或者函数对象,或者任何你可以通过”(…)”符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如: int