网站首页 > 基础教程 正文
std::async()函数异步地运行目标函数,并返回一个std::future对象来携带目标函数的返回值。通过这种方式,async()的工作方式与std::thread非常相似,但允许返回值。
让我们通过几个示例来考虑std::async()的使用。
如何做……
在最简单的形式中,std::async()函数执行的任务与std::thread非常相似,无需调用join()或detach(),同时允许通过std::future对象返回值。
在这个示例中,我们将使用一个函数来计算一定范围内的素数数量。我们将使用chrono::steady_clock来计时每个线程的执行时间。
我们首先定义几个方便的别名:
using launch = std::launch;
using secs = std::chrono::duration<double>;
std::launch具有用于async()调用的执行策略常量。secs别名是一个持续时间类,用于计算我们的素数计算时间。
我们的目标函数计算一定范围内的素数数量。这本质上是通过消耗一些时钟周期来理解执行策略的一种方式:
struct prime_time {
secs dur{};
uint64_t count{};
};
prime_time count_primes(const uint64_t& max) {
// ... 函数实现 ...
}
prime_time结构用于返回值,包含持续时间和计数元素。这允许我们计算循环本身的执行时间。isprime lambda函数在值是素数时返回true。我们使用steady_clock来计算计算素数的循环的持续时间。
在main()中,我们调用我们的函数并报告其计时:
int main() {
constexpr uint64_t MAX_PRIME{ 0x1FFFF };
auto pt = count_primes(MAX_PRIME);
cout << format("primes: {} {:.3}\n", pt.count, pt.dur);
}
输出:
primes: 12252 1.88008s
现在,我们可以使用std::async()异步运行count_primes():
int main() {
constexpr uint64_t MAX_PRIME{ 0x1FFFF };
auto primes1 = async(count_primes, MAX_PRIME);
auto pt = primes1.get();
cout << format("primes: {} {:.3}\n", pt.count, pt.dur);
}
在这里,我们使用count_primes函数和MAX_PRIME参数调用async()。这将在后台运行count_primes()。
async()返回一个std::future对象,该对象携带异步操作的返回值。future对象的get()方法会阻塞,直到异步函数完成,然后返回函数的返回对象。
这与不使用async()时得到的计时几乎相同:
primes: 12252 1.97245s
async()函数可以选择性地将其第一个参数作为执行策略标志:
auto primes1 = async(launch::async, count_primes, MAX_PRIME);
选择是async或deferred。这些标志位于std::launch命名空间中。
async标志启用异步操作,而deferred标志启用延迟求值。这些标志是位映射的,可以使用按位或|运算符组合。
默认设置是两个位都被设置,就好像指定了async | deferred一样。
我们可以使用async()同时运行我们函数的多个实例:
int main() {
// ... 初始化并启动多个异步线程 ...
for(auto& f : swarm) {
auto pt = f.get();
// ... 输出结果 ...
}
// ... 输出总时间 ...
}
我们知道async返回一个future对象。因此,我们可以通过将future对象存储在容器中来运行15个线程。这是我们在运行Windows的6核i7上的输出:
start parallel primes
primes(01): 12252 4.1696s
// ... 更多输出 ...
total duration: 5.9461s
尽管6核i7无法在所有核心上单独运行所有进程,但它仍然在不到6秒的时间内完成了15个实例。
看起来它在大约4秒内完成了前13个线程,然后又花了2秒来完成最后2个线程。它似乎利用了Intel的Hyper-Threading技术,该技术允许在某些情况下在一个核心上运行两个线程。
当我们在12核Xeon上运行相同的代码时,我们得到了这样的结果:
start parallel primes
primes(01): 12252 0.96221s
// ... 更多输出 ...
total duration: 0.98166s
12核Xeon在不到一秒的时间内完成了所有15个进程。
它是如何工作的……
理解std::async的关键在于它使用了std::promise和std::future。
promise类允许一个线程存储一个对象,该对象稍后可以由future对象异步检索。
例如,假设我们有一个这样的函数:
void f() {
cout << "this is f()\n";
}
我们可以使用std::thread来运行它,如下所示:
int main() {
std::thread t1(f);
t1.join();
cout << "end of main()\n";
}
这对于一个简单的、没有返回值的函数来说工作得很好。当我们想从f()返回一个值时,我们可以使用promise和future。
我们在main()线程中设置promise和future对象:
int main() {
std::promise<int> value_promise;
std::future<int> value_future = value_promise.get_future();
std::thread t1(f, std::move(value_promise));
t1.detach();
cout << format("value is {}\n", value_future.get());
cout << "end of main()\n";
}
然后我们将promise对象传递给我们的函数:
void f(std::promise<int> value) {
cout << "this is f()\n";
value.set_value(47);
}
请注意,promise对象不能被复制,所以我们需要使用std::move来传递它。
promise对象作为连接future对象的桥梁,允许我们在值可用时检索它。
std::async()只是一个帮助函数,用于简化promise和future对象的创建。使用async(),我们可以这样做:
int f() {
cout << "this is f()\n";
return 47;
}
int main() {
auto value_future = std::async(f);
cout << format("value is {}\n", value_future.get());
cout << "end of main()\n";
}
这就是async()的价值所在。对于许多目的来说,它使promise和future的使用变得更加容易。
- 上一篇: C++11多线程编程(一)——初始多线程
- 下一篇: C++ 创建线程的方法 c++11创建线程
猜你喜欢
- 2024-11-11 Linux下的C++ socket编程实例 linux c++ tcp
- 2024-11-11 C++11原子变量:线程安全、无锁操作的实例解析
- 2024-11-11 C++11的thread_local原理和应用范例
- 2024-11-11 知识重构-c++ : Lambda 知识重构拼音
- 2024-11-11 c++ 疑难杂症(4) std:vector c++ vector subscript out of range
- 2024-11-11 深入探索C++异步编程的奥秘 c++11异步编程
- 2024-11-11 C++ 开发中使用协程需要注意的问题
- 2024-11-11 golang极速嵌入式Linux应用开发(四)-协程与并发
- 2024-11-11 在计算机编程中,线程是指一个程序内部的执行流程
- 2024-11-11 C++ std:decay、std:bind、std:packaged_task 在模版编程的实践
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)