C++ 11新特性之并发

这篇具有很好参考价值的文章主要介绍了C++ 11新特性之并发。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述

        随着计算机硬件的发展,多核处理器已经成为主流,对程序并发执行能力的需求日益增长。C++ 11标准引入了一套全面且强大的并发编程支持库,为开发者提供了一个安全、高效地利用多核CPU资源进行并行计算的新框架,极大地简化了多线程开发。

std::thread

        在C++ 11中,std::thread是用于创建和管理线程的核心组件。使用线程的一些要点如下。

        1、创建线程。

        通过调用std::thread构造函数,传入要在线程中执行的函数(或可调用对象)以及任何必要的参数来创建线程。

        2、线程函数。

        可以是一个全局函数、类成员函数(此时需要传递指向该类实例的指针或引用),或者是一个满足Callable要求的类型。

        3、线程执行。

        一旦创建了std::thread对象,线程就会尝试启动执行,但具体何时开始执行由操作系统调度决定。

        4、线程同步。

        若多个线程共享数据,通常需要使用互斥锁(std::mutex)、条件变量(std::condition_variable)或其他同步机制来避免竞态条件和数据不一致问题。

        5、线程生命周期管理。

        join()方法会阻塞当前线程,直到被调用join()的线程完成其任务。detach()方法将线程从std::thread对象中分离,使其成为一个守护线程,当主线程退出而未调用join()时,这个分离的线程仍然可以继续运行。但是,如果分离的线程最后仍在运行且没有其他引用,则可能会导致资源泄漏。

        std::thread的具体使用,可参考下面的示例代码。

#include <iostream>
#include <thread>
using namespace std;

void ThreadFunc() 
{
    cout << "Sub thread" << endl;
}

int main() {
    thread myThread(ThreadFunc);
    cout << "Main thread init" << endl;

    myThread.join();
    cout << "Main thread exit" << endl;
    return 0;
}

std::mutex

        std::mutex是用于实现线程间同步的基础工具,它确保同一时间内只有一个线程能够访问被保护的资源或执行一段代码。为了简化锁的管理并防止死锁,通常建议使用std::lock_guard或std::unique_lock这样的RAII(Resource Acquisition Is Initialization)机制来自动管理锁的生命周期。同时,在复杂的情况下,还可以结合条件变量等工具来实现更为灵活的线程同步逻辑。

        在<mutex>头文件中定义了std::mutex类型,它代表一个可重入互斥量。当多个线程试图同时获取已锁定的互斥量时,除了已经持有该互斥量的线程外,其他线程会被阻塞直到互斥量被解锁。std::mutex的主要成员函数如下。

        1、构造函数:默认构造一个未锁定的互斥量。

std::mutex mtx;

        2、lock():将互斥量锁定,如果互斥量已经被另一个线程锁定,则调用此方法的线程将被阻塞,直至互斥量变为可用。

mtx.lock();

        3、unlock():解锁互斥量,允许等待的线程(如果有)获得所有权并继续执行。

mtx.unlock();

        4、try_lock():尝试锁定互斥量,但不会阻塞。如果成功获取锁则返回true,否则(即互斥量已被锁定)立即返回false。

if (mtx.try_lock())
{
    // 已经获取到锁
}
else
{
    // 未能获取到锁
}

        5、RAII包装器类。为了确保即使在异常情况下也能正确释放互斥锁,C++ 11提供了几个基于RAII(Resource Acquisition Is Initialization)原则的包装器类,比如:std::lock_guard和std::unique_lock。

        std::lock_guard:当lock_guard对象创建时自动锁定互斥量,并在其析构时自动解锁互斥量,从而避免忘记解锁导致的死锁问题。

std::lock_guard<std::mutex> lock(mtx);
// 这里是受保护的代码区域
// lock析构时,自动解锁

        std::unique_lock:提供了比lock_guard更多的灵活性,比如:手动锁定、解锁以及尝试锁定等。

std::condition_variable

        condition_variable是C++标准库中的一个同步原语,它是多线程编程中的一种关键工具,主要用于线程间的通信和同步。它与互斥量(mutex)配合使用,可以实现线程的等待和通知机制。具体来说,std::condition_variable类提供了以下的功能。

        1、等待。

        当某个条件不满足时,线程可以调用wait()函数释放互斥锁并进入等待状态,直到其他线程对同一个条件变量调用notify_one()或notify_all()函数唤醒它。

        2、唤醒。

        notify_one():唤醒一个正在等待此条件变量的线程(如果有多个线程在等待,则唤醒其中一个)。

        notify_all():唤醒所有正在等待此条件变量的线程。

        通常,condition_variable的典型使用场景包括:生产者/消费者模式、 barrier同步等,通过它可以有效地控制线程在满足特定条件时才继续执行,从而避免无效的循环检查或者竞争条件等问题。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;

static mutex s_mutex;
static condition_variable s_condition;
static bool s_bReady = false;

void PrintID(int nID)
{
    unique_lock<mutex> lock(s_mutex);
    while (!s_bReady)
    {
        // 当ready为false时,线程会一直等待
        s_condition.wait(lock);
    }

    // 当其他线程修改ready为true,并调用cv.notify_all()后,这里会被唤醒
    cout << "Thread " << nID << " is running." << endl;
}

void Process()
{
    unique_lock<mutex> lock(s_mutex);
    s_bReady = true;
    // 唤醒所有等待的线程
    s_condition.notify_all();
}

int main()
{
    thread pThreads[10];
    for (int i = 0; i < 10; ++i)
    {
        pThreads[i] = thread(PrintID, i + 1);
    }

    // 创建一个线程来修改ready并唤醒其他线程
    thread threadOther(ref(Process));
    for (auto& t : pThreads)
    {
        t.join();
    }

    threadOther.join();
    return 0;
}

        在上面的示例代码中,我们创建了10个线程,它们都在等待一个条件:s_bReady变为true。当Process线程将s_bReady设为true并调用s_condition.notify_all()后,所有等待的线程都会被唤醒,并打印自己的ID。

std::atomic

        std::atomic是C++ 11引入的标准库中的一个模板类,它提供了一种能够在多线程环境中进行原子操作的类型安全方式。原子操作意味着:即使在没有互斥量或其他同步机制的情况下,该操作也能够从多个线程中以不可分割的方式执行,即不会出现半个操作的现象,确保了数据一致性。

        使用std::atomic可以有效地处理简单的同步需求,比如:无锁计数器、标志位等,并且相比传统的互斥锁而言,其开销通常更小,性能更高。

#include <atomic>
#include <thread>
#include <cassert>
using namespace std;

static atomic<int> s_nCounter(0);

void Increment()
{
    ++s_nCounter;
}

int main()
{
    thread t1(Increment);
    thread t2(Increment);

    t1.join();
    t2.join();

    // 这个断言总是成立,因为s_nCounter的递增是原子的
    assert(s_nCounter == 2);
    return 0;
}

        std::atomic支持多种类型的对象,包括但不限于:基本内置类型、指针以及用户自定义类型(如果满足特定条件)。它提供了load、store、exchange、compare_exchange_strong/weak等一系列原子操作方法,用于读写和更新其内部封装的数据成员。

std::future和std::async

        std::future 和 std::async是C++ 11标准引入的异步编程工具,它们位于<future>头文件中,用于简化并发任务的管理和结果的获取。

        std::future是一个模板类,它代表了一个可以在未来某个时间点获取的结果。当你启动一个异步计算时,该计算的结果可以通过std::future对象来访问。std::future 提供了以下功能。

        获取结果:调用std::future::get()会阻塞当前线程,直到异步计算完成并返回结果。

        检查是否已准备好:可以检查future是否已经包含有效结果或异常。

        取消异步操作:虽然不能直接取消异步操作,但可以关联一个可取消的共享状态,然后取消那个状态。

        获取异常:如果异步计算过程中抛出了异常,则可以在future上捕获到这个异常。

        std::async是一个函数模板,它用来异步执行一个函数,并返回一个表示其结果的std::future对象。

#include <iostream>
#include <future>
#include <chrono>
using namespace std;

int LongTimeCompute(int nNumber)
{
    // 模拟耗时操作
    this_thread::sleep_for(chrono::seconds(5));
    return nNumber * nNumber;
}

int main()
{
    // 使用async启动异步任务
    auto future_result = async(launch::async, LongTimeCompute, 10);

    cout << "Main thread running..." << endl;

    // 当需要结果时,调用get()
    int nResult = future_result.get();
    cout << "Result of async computing: " << nResult << endl;
    return 0;
}

        在上面的示例代码中,我们定义了一个模拟耗时计算的函数LongTimeCompute。然后,在main函数中,我们通过std::async创建了一个异步任务,并指定其策略为std::launch::async,确保在新的线程上运行该函数。

        主线程在等待异步任务完成的同时可以继续执行其他任务,当主线程需要得到异步任务的结果时,它调用了 future_result.get(),这将阻塞直到异步计算完成并将结果返回给主线程。最后,主线程输出了异步计算得到的结果。

总结

        C++ 11提供的并发特性不仅简化了多线程编程的复杂性,而且增强了程序的安全性和可靠性。通过合理利用这些工具和技术,我们能够更好地设计和实现适应现代多核架构的应用程序,从而提升软件的整体性能表现。在实践中,还需注意避免死锁、竞态条件等并发问题,并结合实际情况选择适当的并发策略。正确理解和熟练运用C++ 11的并发库,是构建高效、稳定且可扩展应用程序的关键。文章来源地址https://www.toymoban.com/news/detail-830277.html

到了这里,关于C++ 11新特性之并发的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】C++11新特性(下)

      上篇文章(C++11的新特性(上))我们讲述了C++11中的部分重要特性。本篇接着上篇文章进行讲解。本篇文章主要进行讲解: 完美转发、新类的功能、可变参数模板、lambda 表达式、包装器 。希望本篇文章会对你有所帮助。 文章目录 一、完美转发 1、1 实例详解  1、2 应用场

    2024年02月10日
    浏览(33)
  • 【C++】c++11新特性(一)

    目录 { }列表初始化 内置类型---对单值变量及数组的初始化 列表初始化时进行的类型转换 自定义类型---对类对象或结构的初始化 initializer_list 1. 定义接受 initializer_list 参数的构造函数 2. 在函数中使用 initializer_list 参数 3. 使用 initializer_list 与 vector 等容器 用于推断类型的关键

    2024年04月29日
    浏览(30)
  • C++ 11新特性之function

    概述         C++ 11标准库引入了许多创新功能,其中之一便是std::function。作为函数对象容器,std::function允许开发者在编译时不知道具体类型的情况下,存储和传递任意可调用对象,极大地提升了代码的灵活性和可复用性。本文将详细探讨std::function的工作原理、使用场景

    2024年01月18日
    浏览(45)
  • 【C++】—— 简述C++11新特性

    序言: 从本期开始,我将会带大家学习的是关于C++11 新增的相关知识!废话不多说,我们直接开始今天的学习。 目录 (一)C++11简介 (二)统一的列表初始化 1、{}初始化 2、std::initializer_list (三)声明 1、auto 2、decltype 3、nullptr (四)范围for循环 (五)STL中一些变化 总

    2024年02月10日
    浏览(34)
  • C++ 11新特性之bind

    概述         std::bind是C++ 11中functional头文件提供的一个函数模板,它允许我们将函数或成员函数与其部分参数预先绑定在一起,形成一个新的可调用对象(英文为:Callable Object)。这个新的可调用对象可以在后续时机以剩余参数完成调用,这个机制对于事件处理、回调函

    2024年02月21日
    浏览(41)
  • 【C++】C++11新特性的讲解

    新特性讲解第一篇~  文章目录 前言 一、较为重要的新特性 1.统一的初始化列表 2.decltype 3.右值引用+移动语义 总结 C++11 简介 : 在 2003 年 C++ 标准委员会曾经提交了一份技术勘误表 ( 简称 TC1) ,使得 C++03 这个名字已经取代了 C++98 称为 C++11 之前的最新 C++ 标准名称。不

    2024年02月09日
    浏览(39)
  • 【C++基础】C++11的新特性

    C++新特性主要包括包含语法改进和标准库扩充两个方面,主要包括以下11点 1、语法的改进 1)统一的初始化方法 在 C++11 中,可以直接在变量名后面跟上初始化列表,来进行对象的初始化 2)成员变量默认初始化 3)auto** 用于定义变量,编译器可以自动判断的类型(前提

    2024年02月16日
    浏览(30)
  • C++ 11新特性之语法甜点3

    概述         C++ 11中引入了许多简化编程工作的语法上的新特性,我们暂且美其名曰:“语法甜点”。书接上篇,我们继续介绍C++ 11中的这些“语法甜点”,也是第三篇关于“语法甜点”的文章。 语法甜点11:非成员的begin和end         在C++ 03中,标准容器都提供了

    2024年02月20日
    浏览(37)
  • C++ 11新特性之语法甜点4

    概述         C++ 11中引入了许多简化编程工作的语法上的新特性,我们暂且美其名曰:“语法甜点”。书接上篇,我们继续介绍C++ 11中的这些“语法甜点”,也是最后一篇关于“语法甜点”的文章。 语法甜点16:新的字符串字面值         C++ 03提供了两种字符串字面值

    2024年02月20日
    浏览(34)
  • 【C++】C++11的新特性(上)

       C++11作为C++标准的一个重要版本,引入了许多令人振奋的新特性,极大地丰富了这门编程语言的功能和表达能力。本章将为您介绍C++11的一些主要变化和改进,为接下来的章节铺垫。    文章目录 引入 一、列表初始化 1、1 {} 初始化 1、2 std::initializer_list的介绍 二、声明

    2024年02月11日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包