【C++】线程库

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:C++

在这里插入图片描述


前言

C++11标准引入了线程库,通过其可以在C++程序中方便地创建和管理线程。以下是一些常用的线程库组件:

  1. std::threadstd::thread类用于表示一个线程,通过其构造函数可以传入一个可调用对象(函数或者lambda表达式)作为线程的入口点,然后调用join()detach()来等待线程结束或者将线程分离。

  2. std::mutexstd::mutex类用于实现互斥锁防止多个线程同时访问共享资源导致数据竞争。可以通过std::lock_guardstd::unique_lock来管理std::mutex的锁定和解锁。

  3. std::condition_variablestd::condition_variable类用于线程间的条件变量通信,一个线程可以等待另一个线程满足特定条件后再继续执行。

  4. std::atomicstd::atomic模板类提供了原子操作,确保在多线程环境下对共享变量的操作是原子的,避免竞态条件。

  5. std::future 和 std::promisestd::futurestd::promise用于在线程间传递异步操作的结果。std::promise用于设置值,而std::future用于获取这个值。

这些组件提供了丰富的功能,使得在C++程序中使用多线程变得更加容易和安全。


👉🏻thread

std::thread 是 C++11 标准库中用于创建和管理线程的类。下面是 std::thread 类中一些重要的接口函数:

  1. 构造函数

    • explicit thread(Args&&... args);:接受线程函数参数,并在构造函数中启动新线程。
  2. 成员函数

    • void join();:等待线程结束,阻塞当前线程直到被调用的线程执行完毕。
    • bool joinable() const noexcept;:检查线程是否可加入(joinable),即线程对象是否与实际的线程相关联。
    • void detach();:分离线程,允许线程在后台继续执行,线程结束时自动释放资源。
    • std::thread::id get_id() const noexcept;:获取线程的唯一标识符。
    • static unsigned int hardware_concurrency() noexcept;:获取当前系统支持的并发线程数。
  3. 静态成员函数

    • static void yield();:提示调度器放弃当前时间片,允许其他线程执行。
    • static void sleep_for(const std::chrono::duration& rel_time);:使当前线程休眠一段时间。
    • static void sleep_until(const std::chrono::time_point& abs_time);:使当前线程休眠直到指定的时间点。

这些是 std::thread 类中最常用的接口函数,可以帮助你创建、管理和操作线程。当使用 std::thread 时,确保理解这些函数的作用和用法,以便有效地编写多线程程序。


如果你在声明 std::thread 对象时没有传递参数,可以使用 std::thread 对象的 std::thread::joinable() 方法来检查线程是否可以加入。只有在 joinable() 返回 false 的情况下,才能安全地给线程传递参数,否则会导致未定义行为。

你可以通过以下步骤来给已声明但未初始化的线程传递参数:

  1. 检查线程是否可以加入,如果可以,先加入线程。
  2. 重新初始化线程并传递参数。

以下是一个示例:

#include <iostream>
#include <thread>

void threadFunction(int param) {
    std::cout << "Parameter passed to thread: " << param << std::endl;
}

int main() {
    std::thread th1; // 声明但未初始化线程

    int parameter = 42; // 参数值

    // 检查线程是否可以加入
    if (th1.joinable()) {
        th1.join(); // 加入线程
    }

    // 重新初始化线程并传递参数
    th1 = std::thread(threadFunction, parameter);

    // 等待线程执行完毕
    th1.join();

    return 0;
}

在这个示例中,我们首先检查了 th1 线程是否可以加入,如果可以,则先加入线程。然后,我们重新初始化了 th1 线程,并传递了参数给线程函数。最后,我们等待线程执行完毕。
为什么判断完线程是否可以加入后是th1.join(),join不是等待线程回收吗,如果说我判断完线程可以加入,此时直接 th1 = std::thread(threadFunction, parameter)初始化不就行了
答:
这种写法是为了在多线程环境下确保线程安全。因为在某些情况下,可能会出现多个线程同时访问同一个 std::thread 对象的情况,所以为了避免竞态条件,可以先判断线程是否可以加入,再重新初始化线程

成员函数

join() 函数:

  • 函数原型

    void join();
    
  • 参数意义join() 函数没有参数。

  • 返回值void

  • 示例代码

    #include <iostream>
    #include <thread>
    
    void threadFunction() {
        std::cout << "Thread running\n";
    }
    
    int main() {
        std::thread t(threadFunction);
        t.join(); // 等待线程执行完毕
        std::cout << "Main thread\n";
        return 0;
    }
    

    在上面的示例中,join() 函数会阻塞主线程,直到线程 t 执行完毕后才继续执行主线程。

joinable()函数

  • 函数原型

    bool joinable() const noexcept;
    
  • 参数意义joinable() 函数没有参数。

  • 返回值bool,如果线程对象与实际线程相关联,则返回 true;否则返回 false

  • 示例代码

    #include <iostream>
    #include <thread>
    
    int main() {
        std::thread t;
        std::cout << "Is thread joinable? " << (t.joinable() ? "Yes" : "No") << std::endl;
        
        t = std::thread([](){
            std::cout << "Thread running\n";
        });
        
        std::cout << "Is thread joinable? " << (t.joinable() ? "Yes" : "No") << std::endl;
        
        t.join();
        std::cout << "Is thread joinable? " << (t.joinable() ? "Yes" : "No") << std::endl;
        
        return 0;
    }
    

    在这个示例中,先输出 No,表示初始时线程 t 不可加入;然后创建线程并输出 Yes;最后再次输出 No,表示线程在调用 join() 后不可再次加入。

detach函数

  • 函数原型

    void detach();
    
  • 参数意义detach() 函数没有参数。

  • 返回值void

  • 示例代码

    #include <iostream>
    #include <thread>
    
    void threadFunction() {
        std::cout << "Thread running\n";
    }
    
    int main() {
        std::thread t(threadFunction);
        t.detach(); // 分离线程
        // 注意:分离后不能再调用join(),否则会导致程序终止
        
        std::this_thread::sleep_for(std::chrono::seconds(1)); // 等待子线程执行完毕(非最佳做法)
        
        std::cout << "Main thread\n";
        return 0;
    }
    

    在这个示例中,线程 t 被分离后,主线程不再等待其执行完毕,而是继续执行后续代码。

get_id()函数

  • 函数原型

    std::thread::id get_id() const noexcept;
    
  • 参数意义get_id() 函数没有参数。

  • 返回值std::thread::id,表示线程的唯一标识符。

  • 示例代码

    #include <iostream>
    #include <thread>
    
    int main() {
        std::thread t([]{
            std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
        });
        
        std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
        
        t.join();
        return 0;
    }
    

    这个示例演示了如何使用 get_id() 函数获取线程的唯一标识符,并输出主线程和子线程的 ID。

静态成员函数

std::this_thread::yield()函数

  • 函数原型

    void yield();
    
  • 参数意义yield() 函数没有参数。

  • 返回值void

  • 功能yield() 函数会将当前线程放弃处理器,以便其他线程有机会执行。调用此函数会暗示操作系统调度器立即切换到另一个可运行的线程。

  • 示例代码

    #include <iostream>
    #include <thread>
    
    void threadFunction() {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread running\n";
            std::this_thread::yield(); // 放弃处理器
        }
    }
    
    int main() {
        std::thread t(threadFunction);
        for (int i = 0; i < 5; ++i) {
            std::cout << "Main thread\n";
            std::this_thread::yield(); // 放弃处理器
        }
        t.join();
        return 0;
    }
    

    在这个示例中,yield() 函数被用来让出处理器,使得线程间能够交替执行。

std::this_thread::sleep_for()函数

  • 函数原型

    template< class Rep, class Period >
    void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration );
    
  • 参数意义sleep_duration 表示休眠的时间段,可以是 std::chrono::milliseconds, std::chrono::seconds 等类型。

  • 返回值void

  • 功能sleep_for() 函数会使当前线程休眠指定的时间段。

  • 示例代码

    #include <iostream>
    #include <thread>
    #include <chrono>
    
    int main() {
        std::cout << "Main thread starts sleeping\n";
        std::this_thread::sleep_for(std::chrono::seconds(3)); // 休眠3秒
        std::cout << "Main thread wakes up\n";
        return 0;
    }
    

    在这个示例中,主线程调用 sleep_for() 函数休眠3秒后再继续执行。

std::this_thread::sleep_until()函数

  • 函数原型

    template< class Clock, class Duration >
    void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
    
  • 参数意义sleep_time 表示将要休眠到的时间点,通常通过 std::chrono::system_clock::now() + duration 来计算。

  • 返回值void

  • 功能sleep_until() 函数会使当前线程休眠直到指定的时间点。

  • 示例代码

    #include <iostream>
    #include <thread>
    #include <chrono>
    
    int main() {
        auto wakeUpTime = std::chrono::system_clock::now() + std::chrono::seconds(5);
        std::cout << "Main thread starts sleeping\n";
        std::this_thread::sleep_until(wakeUpTime); // 休眠直到指定时间点
        std::cout << "Main thread wakes up\n";
        return 0;
    }
    

    在这个示例中,主线程调用 sleep_until() 函数休眠直到指定的时间点后再继续执行。

为什么thread的传参里面如果想传引用一定要用std::ref?

在C++中,std::ref是用于包装引用的模板函数,它位于<functional>头文件中。当你需要将一个引用传递给函数或者线程时,有时候需要使用std::ref来确保正确的引用传递语义。

通常情况下,当你将一个变量作为参数传递给函数或者线程时,会发生值复制。但是有时候你希望传递的实际上是引用本身,而不是引用指向的值。这时就可以使用std::ref来包装引用,以便进行正确的引用传递。

举个例子,假设有一个函数void func(int& val)接受一个整型引用作为参数,如果你想在创建线程时将某个整型变量x的引用传递给这个函数,可以这样做:

int x = 42;
std::thread t(func, std::ref(x));

在这个例子中,std::ref(x)x的引用包装起来,然后将这个包装后的引用传递给func函数,确保了在新线程中对x的引用传递。


介绍完ref后,我们来回答开始的问题:

在C++中,当你创建线程时,传递参数给线程的方式是通过复制参数值来实现的。如果你想传递引用而不是值,直接将引用传递给std::thread是不安全的,因为线程的执行可能会在原始变量(引用指向的对象)被销毁之后才开始,导致悬空引用或者未定义行为

使用std::ref包装引用的原因在于它可以延长被引用对象的生命周期std::ref返回一个std::reference_wrapper对象,该对象在复制时只是简单地复制了引用,而不是引用指向的内容。这样可以确保被引用对象在线程执行期间保持有效,并在线程完成后才会被销毁。

因此,为了避免悬空引用或者未定义行为,当你想要在线程中传递引用时,必须使用std::ref或者std::cref来保证被引用对象的有效性,从而确保线程安全地访问被引用的对象。

👉🏻mutex

基本接口函数

当涉及到多线程编程时,std::mutex 是一个常用的互斥量类,用于保护共享资源,避免多个线程同时访问导致数据竞争。以下是 std::mutex 类提供的一些主要接口函数:

  1. std::mutex 构造函数

    • 函数原型
      mutex();
      
    • 功能:创建一个新的互斥量对象。
  2. lock() 函数

    • 函数原型
      void lock();
      
    • 功能:尝试锁定互斥量,如果互斥量当前没有被其他线程锁定,则当前线程将锁定互斥量;如果互斥量已经被锁定,当前线程将被阻塞直到互斥量可用。
  3. try_lock() 函数

    • 函数原型
      bool try_lock();
      
    • 功能:尝试去锁定互斥量,如果互斥量当前没有被其他线程锁定,则当前线程将锁定互斥量并返回 true;如果互斥量已经被锁定,try_lock() 立即返回 false 而不会阻塞当前线程。
  4. unlock() 函数

    • 函数原型
      void unlock();
      
    • 功能:解锁互斥量,允许其他线程尝试锁定该互斥量。

下面是一个简单的示例,演示了如何使用 std::mutex 来保护共享资源:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int sharedData = 0;

void updateSharedData() {
    mtx.lock();
    sharedData++;
    std::cout << "Thread ID: " << std::this_thread::get_id() << " updated sharedData to: " << sharedData << std::endl;
    mtx.unlock();
}

int main() {
    std::thread t1(updateSharedData);
    std::thread t2(updateSharedData);

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

    return 0;
}

在这个示例中,两个线程 t1t2 分别尝试更新 sharedData 变量,通过 std::mutex 对这一操作进行保护。这样可以确保在任一时刻只有一个线程可以访问和修改 sharedData,避免了数据竞争问题。

lock_guard和unique_lock

std::lock_guardstd::unique_lock 都是 C++ 中用于管理互斥锁(mutex)的 RAII(资源获取即初始化)类,它们可以帮助确保在作用域结束时自动释放互斥锁,避免忘记手动解锁而导致的死锁等问题。

  1. std::lock_guard

    • std::lock_guard 是一个轻量级的互斥锁封装类,一旦被创建,它会自动锁定传入的互斥锁,并在其作用域结束时自动解锁。
    • std::lock_guard 适用于那些在同一作用域内需要加锁和解锁的场景它不能手动释放锁,只能在作用域结束时自动释放锁。
  2. std::unique_lock

    • std::unique_lock 提供了比 std::lock_guard 更灵活的功能。它不仅可以管理互斥锁的锁定和解锁,还可以手动地进行锁定和解锁。
    • std::unique_lock 支持延迟加锁和条件变量,因此在需要更灵活控制锁的场景下更为适用。

在使用这两个类时,当创建一个 std::lock_guardstd::unique_lock 对象时,会在构造函数中锁定传入的互斥锁,并在对象销毁时(作用域结束)自动解锁。

以下是一个简单的示例,演示了如何使用 std::lock_guardstd::unique_lock 来管理互斥锁:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void workFunction() {
    std::lock_guard<std::mutex> lockGuard(mtx); // 使用 std::lock_guard 自动管理锁的加锁和解锁

    // 这里可以安全地访问共享资源,因为锁已经被自动加锁

    std::cout << "Worker thread is processing...\n";
}

int main() {
    std::thread workerThread(workFunction);

    // 主线程也可以使用 std::unique_lock 手动管理锁的加锁和解锁
    std::unique_lock<std::mutex> uniqueLock(mtx);
    
    // 这里可以安全地访问共享资源,因为锁已经被手动加锁

    // 在 uniqueLock 对象生命周期结束时,互斥锁会被自动解锁

    workerThread.join();

    return 0;
}

在这个示例中,workFunction 函数通过 std::lock_guard 管理互斥锁,而主线程通过 std::unique_lock 手动管理互斥锁


RAII(Resource Acquisition Is Initialization)是一种重要的 C++ 编程技术,用于管理资源的分配和释放。RAII 的核心思想是:通过在对象的构造函数中获取资源(如内存、文件句柄、互斥锁等),在对象的析构函数中释放资源,从而确保资源在对象生命周期结束时被正确释放。

使用 RAII 技术可以避免资源泄漏和忘记释放资源导致的问题,使得资源的管理更加安全和简单。当对象被创建时,它自动获取资源;当对象超出作用域时,其析构函数会自动调用,确保资源被释放。这种自动化的资源管理方式使得代码更加健壮并且易于维护。

常见的使用 RAII 的情况包括:

  1. 使用 std::unique_ptrstd::shared_ptr 等智能指针来管理动态分配的内存。
  2. 使用 std::lock_guardstd::unique_lock 等类来管理互斥锁的加锁和解锁。
  3. 使用文件流对象来管理文件的打开和关闭。
  4. 使用自定义的 RAII 类来管理其他资源,如数据库连接、网络连接等。

RTTI(Run-Time Type Information)是一个 C++ 的特性,用于在运行时获取对象的类型信息。通过 RTTI,你可以在程序运行时查询对象的实际类型,以及确定对象是否属于某个特定的类或子类。

C++ 中的 RTTI 主要通过两个关键字来实现:

  1. dynamic_cast:用于在继承体系中进行安全的向下转换(downcast),即将基类指针或引用转换为派生类指针或引用。如果转换不安全,dynamic_cast会返回空指针(对于指针)或抛出std::bad_cast异常(对于引用)。

  2. typeid:用于获取对象的类型信息。通过typeid操作符,可以获得一个指向std::type_info的对象,从而可以比较两个对象的类型是否相同。

这些特性允许你在运行时进行类型检查和类型转换,通常用于处理多态对象、工厂模式、插件系统等场景。需要注意的是,RTTI 的使用可能会引入一些运行时开销,并且过度依赖 RTTI 也可能暗示设计上的缺陷。因此,在使用 RTTI 时需要权衡好利弊,确保它符合程序设计的需要。

总的来说,RTTI 是 C++ 提供的一种功能强大的特性,可以在某些情况下帮助你更加灵活地处理对象的类型信息,但需要慎重使用以避免过度复杂化代码结构。


👉🏻condition_variable

当涉及到多线程编程中的线程同步和通信时,std::condition_variable 是一个重要的工具,用于实现线程间的条件变量等待和通知机制。下面是 std::condition_variable 类提供的一些主要接口函数:

  1. std::condition_variable 构造函数

    • 函数原型
      condition_variable();
      
    • 功能:创建一个新的条件变量对象。
  2. wait() 函数

    • 函数原型
      void wait(std::unique_lock<std::mutex>& lock,Predicate pred);
      
    • 功能:当前线程等待条件变量,同时释放互斥锁 lock,使得其他线程可以获取互斥锁并修改共享数据。当收到通知后,wait() 函数会重新获取互斥锁 lock 并继续执行。
      pred 参数通常是一个函数对象(比如 lambda 表达式),用来判断条件是否满足。当 pred 返回 false 时,wait 函数将会阻塞当前线程。一旦条件满足,wait 函数将重新获取锁,并使当前线程继续执行。
  3. notify_one() 函数

    • 函数原型
      void notify_one();
      
    • 功能:唤醒等待在条件变量上的一个线程,如果有多个线程在等待,则只会唤醒其中一个线程。
  4. notify_all() 函数

    • 函数原型
      void notify_all();
      
    • 功能:唤醒等待在条件变量上的所有线程。

下面是一个简单的示例,演示了如何使用 std::condition_variable 进行线程同步和通信:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void workerThread() {
    std::unique_lock<std::mutex> lock(mtx);
    while (!ready) {
        cv.wait(lock);
    }
    std::cout << "Worker thread is processing...\n";
}

int main() {
    std::thread worker(workerThread);

    // 模拟一些工作的完成
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        std::cout << "Main thread notifies worker thread\n";
    }
    
    cv.notify_one();

    worker.join();

    return 0;
}

在这个示例中,主线程通过设置 readytrue,并调用 notify_one() 唤醒等待在条件变量 cv 上的工作线程。工作线程在收到通知后被唤醒,然后开始处理任务。


当谈到条件变量在多线程中的作用时,我们可以通过一个幽默的例子来理解它的用处。

假设有一个办公室里有两个员工:小明和小红。他们的工作是协作完成一份文件,小明负责写文档,小红负责审核文档。他们需要保持协调,即小明写完了文档,小红要及时审核,并且在小红审核完后,小明要能够继续写新的文档。

这里,小明和小红就相当于两个线程,他们需要协作完成工作,而条件变量就是用来协调这种工作流程的。具体来说:

  • 小明写完文档后,他会等待条件变量,即等待小红的审核通知。
  • 小红审核完文档后,她会通知小明,同时唤醒小明继续写新的文档。

这个例子中,小明和小红就像是两个线程,他们通过条件变量来同步工作,保证了写和审核工作的顺利进行,避免了资源的浪费和不必要的等待。同时,这个例子也生动地展示了条件变量在多线程场景下的作用,希望能够帮助你更好地理解条件变量的使用。

👉🏻代码两个线程实现奇偶打印

code:

#include<iostream>
#include<vector>
#include<string>
using namespace std;

#include<mutex>
#include<thread>

int main()
{
	mutex mtx;
	condition_variable cv;
	bool flag = false;
	int x = 0;
	thread t1([&]() {
		for (size_t i = 0; i < 10; i++)
		{

			unique_lock<mutex> lock(mtx);//上锁
			while (flag)//while循环反复判断,一旦flag被改,可以马上感知
				cv.wait(lock);//线程等待,释放互斥锁,等待唤醒
			cout <<  "t1:" << x << endl;
			++x;
			flag = true;

			cv.notify_one();//唤醒等待队列中的一个线程

		}
		});

	thread t2([&]() {
		for (size_t i = 0; i < 10; i++)
		{

			unique_lock<mutex> lock(mtx);//上锁
			while(!flag)
				cv.wait(lock);//线程等待,释放互斥锁,等待唤醒
			cout <<  "t2:" << x << endl;
			++x;
			flag = false;

			cv.notify_one();//唤醒等待队列中的一个线程

		}
		});

	t1.join();//join() 函数会阻塞主线程,直到线程 t1 执行完毕后才继续执行主线程
	t2.join();
	return 0;
}

实现效果:
在这里插入图片描述
两个场景分析:
在这里插入图片描述
总而言之就是利用flag、wait条件变量的配合,使得两个线程有序的进行对x进行交替操作

👉🏻CAS

CAS(Compare and Swap)是一种并发编程中常用的原子操作,用于实现多线程间的同步。CAS 操作包括三个操作数:内存地址 V旧的预期数值 A 新的数值 BCAS 操作会比较内存地址 V 的当前值与预期值 A,如果相等,则将内存地址 V 的值更新为新值 B;如果不相等,则不做任何操作。

CAS 操作通常用于实现无锁数据结构并发算法,它能够保证在高并发情况下对共享数据的原子性操作,避免了使用传统的加锁机制可能引起的性能损失和死锁问题

对共享数据进行加锁操作,就是因为对共享数据的操作是非原子操作,会影响数据的一致性,但如果对共享数据的操作是原子操作,就没必要进行加锁操作了

CAS 操作的基本思想是利用硬件提供的原子性操作指令来实现,确保在执行过程中不会被其他线程打断,从而保证了操作的原子性

在 C++11 之后,标准库也提供了一些原子操作的封装,比如 std::atomic 类模板,其中就包含了 CAS 操作的实现。通过 std::atomic 可以很方便地进行原子操作,避免了手动编写平台相关的 CAS 指令。

总的来说,CAS 是一种用于实现多线程同步的技术,通过比较和交换操作来保证对共享数据的原子性访问,在并发编程中扮演着重要的角色。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

👉🏻atomic

概念

std::atomic 是 C++11 标准库提供的用于支持原子操作的模板类,可以确保对共享数据进行原子操作,避免数据竞争和提供并发访问的线程安全性。

以下是 std::atomic 的一些特点和用法:

  1. 原子类型支持的操作std::atomic 支持各种原子操作,如读取、写入、交换、比较交换等,这些操作都是原子的,不会被中断。常见的原子类型包括 std::atomic<int>, std::atomic<bool>, std::atomic<double> 等。

  2. 使用方法:使用 std::atomic 非常简单,只需简单声明一个std::atomic对象,然后就可以像操作普通变量一样来对待它。例如:

    std::atomic<int> atomicInt(0);
    atomicInt.store(42); // 存储操作
    int value = atomicInt.load(); // 加载操作
    
  3. 原子操作保证std::atomic 中的操作是原子的,不需要额外的锁保护,可以在多线程环境下安全地对共享数据进行操作。

  4. 内存顺序std::atomic 还提供了内存顺序的控制,可以通过指定不同的内存顺序来控制原子操作的内存可见性和顺序性。

  5. 适用场景std::atomic 适用于简单的原子操作,能够有效地避免数据竞争问题,提高多线程程序的性能。

常见接口函数介绍

  1. load():

    • 函数原型:
    • T load(memory_order order = memory_order_seq_cst) const noexcept;
    • 参数意义:order 表示内存顺序,默认为 memory_order_seq_cst,指定对原子操作的内存顺序要求。
    • 使用示例代码:
      int value = atomicInt.load(std::memory_order_relaxed);
      
  2. store(val):

    • 函数原型:
    • void store(T val, memory_order order = memory_order_seq_cst) noexcept;
    • 参数意义:val 表示要存储的值,order 表示内存顺序,默认为 memory_order_seq_cst。
    • 使用示例代码:
      atomicInt.store(42, std::memory_order_relaxed);
      
  3. exchange(val):

    • 函数原型:
    • T exchange(T val, memory_order order = memory_order_seq_cst) noexcept;
    • 参数意义:val 表示要替换的新值,order 表示内存顺序,默认为 memory_order_seq_cst。
    • 使用示例代码:
      int oldValue = atomicInt.exchange(50, std::memory_order_relaxed);
      
  4. compare_exchange_weak(expected, desired):

    • 函数原型:
    • bool compare_exchange_weak(T& expected, T desired, memory_order success, memory_order failure) noexcept;
    • 参数意义:expected 是比较的预期值,desired 是要替换的新值,success 和 failure 分别表示成功和失败时的内存顺序。
    • 使用示例代码:
      int expected = 0;
      int desired = 10;
      bool success = atomicInt.compare_exchange_weak(expected, desired, std::memory_order_relaxed, std::memory_order_relaxed);
      
  5. compare_exchange_strong(expected, desired):

    • 函数原型:
    • bool compare_exchange_strong(T& expected, T desired, memory_order success, memory_order failure) noexcept;
    • 参数意义:与 compare_exchange_weak 相同,只是使用更强的内存顺序保证。
    • 使用示例代码:
      int expected = 0;
      int desired = 20;
      bool success = atomicInt.compare_exchange_strong(expected, desired, std::memory_order_relaxed, std::memory_order_relaxed);
      

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值