0


C++中确保线程安全的几种方式

在 C++ 中,可以使用以下几种方式来确保线程安全:

  1. 使用互斥量(mutex)来对共享资源进行保护。互斥量可以用来防止多个线程同时访问共享资源,从而避免数据竞争的问题。
  2. 使用读写锁(reader-writer lock)来对共享资源进行保护。读写锁允许多个读线程同时访问共享资源,但是写线程必须独占资源。这样可以在保证线程安全的同时,也尽可能地提高系统的并发性。
  3. 使用原子操作来对共享资源进行保护。在 C++ 中,可以使用 std::atomic 类型来定义原子变量,并使用原子操作来对共享资源进行操作。这样可以确保在多线程环境中,原子变量的操作是安全的。
  4. 使用条件变量(condition variable)来协调线程间的协作。条件变量可以用来在线程之间传递信号,从而控制线程的执行流程。
  5. 使用线程本地存储(thread-local storage)来保存线程的私有数据。线程本地存储可以用来给每个线程分配独立的存储空间,从而避免数据冲突的问题。

1.使用互斥量(mutex)来保护共享资源:

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

std::mutex g_mutex;// 全局互斥量int g_counter =0;// 共享资源voidincrementCounter(){
    std::lock_guard<std::mutex>lock(g_mutex);// 上锁++g_counter;}intmain(){
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);

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

    std::cout <<"g_counter = "<< g_counter << std::endl;return0;}
  1. 在这个例子中,我们定义了一个全局互斥量 g_mutex 和一个共享资源 g_counter。然后在 incrementCounter 函数中,我们使用 std::lock_guard 对 g_mutex 进行加锁。这样可以保证在同一时刻,只有一个线程可以访问 g_counter。
  2. 在主函数中,我们创建了两个线程 t1 和 t2,并让它们都执行 incrementCounter 函数。由于 g_mutex 加锁的作用,所以只有一个线程能够修改 g_counter 的值,因此最终的结果就是 g_counter = 2。

2. 使用读写锁(reader-writer lock)来保护共享资源:

#include<iostream>#include<thread>#include<shared_mutex>

std::shared_mutex g_mutex;// 全局读写锁int g_counter =0;// 共享资源voidincrementCounter(){
    std::unique_lock<std::shared_mutex>lock(g_mutex);// 上写锁++g_counter;}voidreadCounter(){
    std::shared_lock<std::shared_mutex>lock(g_mutex);// 上读锁
    std::cout <<"g_counter = "<< g_counter << std::endl;}intmain(){
    std::thread t1(incrementCounter);
    std::thread t2(readCounter);

    t1.join();
    t2.join();return0;}
  1. 在这个例子中,我们定义了一个全局读写锁 g_mutex 和一个共享资源 g_counter。然后在 incrementCounter 函数中,我们使用 std::unique_lock 对 g_mutex 进行加写锁。这样可以保证在同一时刻,只有一个线程可以修改 g_counter 的值。
  2. 在 readCounter 函数中,我们使用 std::shared_lock 对 g_mutex 进行加读锁。这样可以保证在同一时刻,可以有多个线程同时读取 g_counter 的值,但是写线程必须等待所有的读线程结束后才能执行。

3.使用原子操作来保护共享资源:

#include<iostream>#include<thread>#include<atomic>

std::atomic<int> g_counter{0};// 原子变量voidincrementCounter(){++g_counter;}intmain(){
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);

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

    std::cout <<"g_counter = "<< g_counter.load()<< std::endl;return0;}
  1. 在上面的例子中,我们定义了一个共享资源 g_counter,并使用 std::atomic 类型声明。然后在 incrementCounter 函数中,我们使用了 ++g_counter 进行原子操作,以保证在多线程环境下访问 g_counter 是安全的。
  2. 在主函数中,我们创建了两个线程 t1 和 t2,分别执行 incrementCounter 函数。由于 g_counter 是一个原子类型,所以在多线程环境下对其进行操作是安全的。最后,我们在主线程中输出了 g_counter 的值,显示了线程安全的结果。
  3. 请注意,在使用原子操作时,需要根据具体情况选择合适的原子类型。例如,如果需要操作整型,可以使用 std::atomic 类型;如果需要操作布尔型,可以使用 std::atomic 类型。

4.使用条件变量(condition variable)来协调线程间的协作:

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

std::mutex g_mutex;
std::condition_variable g_cv;bool g_flag =false;voidthread1(){
    std::unique_lock<std::mutex>lock(g_mutex);
    g_flag =true;
    g_cv.notify_one();// 通知线程 2}voidthread2(){
    std::unique_lock<std::mutex>lock(g_mutex);while(!g_flag){
        g_cv.wait(lock);// 等待通知}
    std::cout <<"thread 2 finished"<< std::endl;}intmain(){
    std::thread t1(thread1);
    std::thread t2(thread2);

    t1.join();
    t2.join();return0;}
  1. 在这个例子中,我们定义了一个互斥量 g_mutex 和一个条件变量 g_cv。我们还定义了一个全局变量 g_flag,用于标记某个条件是否满足。
  2. 在线程 1 中,我们将 g_flag 设为 true,并使用 g_cv.notify_one() 函数通知线程 2。
  3. 在线程 2 中,我们使用 while (!g_flag) 循环检测 g_flag 的值。如果 g_flag 为 false,则使用 g_cv.wait(lock) 函数等待通知,否则执行后续的操作。
  4. 当线程 1 通知线程 2 时,线程 2 将被唤醒,并继续往下执行。最终,线程 2 会输出 “thread 2 finished”。
  5. 通过这个例子,我们可以看到,使用条件变量可以在线程间协调协作,使得线程可以根据某些条件的改变而被唤醒或等待。

5.使用线程本地存储(thread-local storage)来保存线程的私有数据:

#include<iostream>#include<thread>thread_localint g_counter =0;// 线程本地变量voidincrementCounter(){++g_counter;
    std::cout << std::this_thread::get_id()<<": "<< g_counter << std::endl;}intmain(){
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);

    t1.join();
    t2.join();return0;}
  1. 在这个例子中,我们使用了 thread_local 关键字声明了线程本地变量 g_counter。在主函数中,我们创建了两个线程 t1 和 t2,分别执行 incrementCounter 函数。
  2. 在 incrementCounter 函数中,我们使用了 ++g_counter 对 g_counter 进行了修改。由于 g_counter 是线程本地变量,所以每个线程都有自己的 g_counter,互不干扰。
  3. 最后,我们使用了 std::cout << std::this_thread::get_id() << ": " << g_counter << std::endl; 输出了每个线程的线程 ID 和 g_counter 的值。
  4. 编译并运行这个程序,可以看到每个线程的 g_counter 都是线程本地的,互不干扰。这就是使用线程本地存储来保存线程的私有数据的例子。
标签: c++ 开发语言

本文转载自: https://blog.csdn.net/u013820121/article/details/128542514
版权归原作者 从三岁开始。。。 所有, 如有侵权,请联系我们删除。

“C++中确保线程安全的几种方式”的评论:

还没有评论