(C++11/14/17学习笔记):std::atomic续、std::async与std::thread对比
发布日期:2021-05-07 15:19:55 浏览次数:11 分类:精选文章

本文共 3269 字,大约阅读时间需要 10 分钟。

std::atomic续谈与std::async与std::thread对比

原子操作std::atomic续谈

在多线程编程中,原子操作是保证在多个线程之间保持一致性和正确性的关键。C++中的std::atomic提供了一种轻量级的原子操作机制,适用于处理不可线性化的变量操作。以下是关于std::atomic的一些关键点:

  • 原子操作的支持操作

    std::atomic支持以下操作:++--+==|=^=。这些操作在多线程环境下保证原子性,防止数据竞争和舍去。例如,my_count += 1 会比 my_count = my_count + 1 更安全,因为前者不会在操作过程中被打断。

  • 原子操作的实现方式

    可以通过std::atomic<int>类来封装整数类型的变量,像操作普通变量一样使用。例如:

    #include 
    #include
    #include
    int my_count{ 0 }; // 使用std::atomic
    封装int变量void my_thread() { for (int i = 0; i < 1000000; ++i) { my_count += 1; // 原子操作,保证线程安全 }}int main() { std::thread threadObj1(my_thread); std::thread threadObj2(my_thread); threadObj1.join(); threadObj2.join(); std::cout << "两个线程执行完毕,最终 my_count的值是" << my_count << std::endl; return 0;}

    通过上述代码可以看到,多个线程同时对my_count进行操作时,结果仍然是正确的。

  • 注意事项

    如果使用my_count = my_count + 1,则不一定保证原子性,因为在某些情况下可能会导致不可见的读写冲突。因此,建议在多线程环境下始终使用支持的原子操作符。


std::async与std::thread的区别

std::async和std::thread都是用于在C++中创建和管理线程的工具,但它们在工作机制和使用方式上有显著的不同。以下是详细对比:

1. std::async的参数和作用

std::async接受一个入口函数作为参数,可以通过以下方式使用:

#include 
#include
#include
using namespace std;future
result = async(mythread);// 或者future
result = async(std::launch::async, mythread);
  • 参数说明

    • std::launch::deferred:延迟调用,异步任务不会立即执行。
    • std::launch::async:强制在新线程上执行异步任务。
    • 默认参数:std::launch::async | std::launch::deferred,系统会自行选择策略。
  • 作用

    std::async创建的是一个异步任务,而不是直接创建线程。任务的执行方式取决于系统的资源情况:

    • 如果资源充足,系统可能选择在新线程上执行。
    • 如果资源紧张,系统可能选择延迟执行任务。

2. std::thread的特点

  • 资源消耗:std::thread会直接创建新线程。如果系统资源紧张,可能会抛出异常,导致程序崩溃。
  • 线程管理:创建线程后,必须调用join()来等待线程完成,否则可能导致资源泄漏。
  • 返回值:线程函数返回值不易获取,通常需要通过共享变量的方式传递。

3. std::async的灵活性

  • 资源管理:如果系统资源紧张,std::async不会强制创建线程,而是将任务推迟到future的get()wait()方法调用时执行。
  • 线程调度:异步任务可以在等待future结果的线程上自动执行,这避免了不必要的线程切换开销。

4. 资源限制的影响

  • std::thread:线程数量过多可能导致系统资源耗尽,甚至崩溃。
  • std::async:如果系统资源紧张,异步任务可能不会立即创建线程,而是等待future的调用执行。这种方式在资源受限的情况下更可靠。

std::async的不确定性问题

在使用std::async时,可能会遇到不确定性问题。例如,系统可能选择使用std::launch::deferred(延迟调用)或std::launch::async(立即创建新线程)。要解决这种不确定性,可以通过以下方式判断任务执行情况:

示例代码

#include 
#include
#include
#include
using namespace std;int mythread() { cout << "mythread start" << "ThreadId = " << this_thread::get_id() << endl; cout << "mythread end" << "ThreadId = " << this_thread::get_id() << endl; return 100;}int main() { cout << "MainThreadID = " << this_thread::get_id() << endl; future
result = async(mythread); future_status status = result.wait_for(0s); // 等待0秒判断状态 if (status == future_status::deferred) { cout << result.get() << endl; } else if (status == future_status::ready) { cout << "线程成功执行完毕并返回!" << endl; cout << result.get() << endl; } else if (status == future_status::timeout) { cout << "超时线程没执行完!" << endl; cout << result.get() << endl; } return 0;}

通过result.wait_for(0s),可以判断异步任务的执行方式:

  • future_status::deferred:任务被延迟执行。
  • future_status::ready:任务已经完成。
  • future_status::timeout:任务未完成,但超时。

总结

std::async与std::thread在多线程编程中的应用场景有显著不同。std::async更注重灵活性和资源管理,适合在资源有限的情况下使用。通过合理配置launch策略和检查future状态,可以有效应对异步任务的不确定性问题。在实际开发中,应根据具体需求选择适合的线程创建方式,同时注意资源管理和状态判断,以确保程序的稳定性和性能。

上一篇:(UNIX网络编程学习笔记):UDP概述、recvfrom和sendto函数及案例
下一篇:(C++11/14/17学习笔记):future其他成员函数、shared_future、atomic

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月11日 03时01分09秒