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

这篇具有很好参考价值的文章主要介绍了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;   // 共享资源

void incrementCounter()
{
    std::lock_guard<std::mutex> lock(g_mutex);  // 上锁
    ++g_counter;
}

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

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

    std::cout << "g_counter = " << g_counter << std::endl;

    return 0;
}

  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;   // 共享资源

void incrementCounter()
{
    std::unique_lock<std::shared_mutex> lock(g_mutex);  // 上写锁
    ++g_counter;
}

void readCounter()
{
    std::shared_lock<std::shared_mutex> lock(g_mutex);  // 上读锁
    std::cout << "g_counter = " << g_counter << std::endl;
}

int main()
{
    std::thread t1(incrementCounter);
    std::thread t2(readCounter);

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

    return 0;
}

  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};  // 原子变量

void incrementCounter()
{
    ++g_counter;
}

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

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

    std::cout << "g_counter = " << g_counter.load() << std::endl;

    return 0;
}

  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;

void thread1()
{
    std::unique_lock<std::mutex> lock(g_mutex);
    g_flag = true;
    g_cv.notify_one();  // 通知线程 2
}

void thread2()
{
    std::unique_lock<std::mutex> lock(g_mutex);
    while (!g_flag)
    {
        g_cv.wait(lock);  // 等待通知
    }
    std::cout << "thread 2 finished" << std::endl;
}

int main()
{
    std::thread t1(thread1);
    std::thread t2(thread2);

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

    return 0;
}

  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_local int g_counter = 0;  // 线程本地变量

void incrementCounter()
{
    ++g_counter;
    std::cout << std::this_thread::get_id() << ": " << g_counter << std::endl;
}

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

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

    return 0;
}

  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 都是线程本地的,互不干扰。这就是使用线程本地存储来保存线程的私有数据的例子。

文章来源地址https://www.toymoban.com/news/detail-415978.html

到了这里,关于C++中确保线程安全的几种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • python - 线程的启动的几种方式

    本文主要给大家介绍python启动线程的四种方式 创建 Thread 对象,然后调用 start() 方法启动线程。 重写 run() 方法,并调用 start() 方法启动线程。 使用ThreadPoolExecutor 类的 submit() 方法提交任务,自动创建线程池并执行任务。 创建进程,然后在进程中启动线程。 以上就是python中启

    2024年02月12日
    浏览(45)
  • Java 中创建线程的几种方式

    Java 是一种面向对象的编程语言,它支持多线程编程。多线程编程是指在一个程序中同时运行多个线程,这些线程可以并行执行,以提高程序的效率和性能。Java 提供了多种创建线程的方法,本文将介绍这些方法以及它们的优缺点。 Java 中的 Thread 类是一个抽象类,我们可以通

    2024年02月09日
    浏览(53)
  • java 实现开启异步线程的几种方式

    在Java中,有多种方式可以实现异步线程以避免在主线程中执行耗时操作导致界面卡顿的问题。以下是几种常用的方式: 使用 Thread 类:可以使用 Thread 类来创建一个新的线程,并在其 run() 方法中执行耗时操作。例如: 使用 Runnable 接口:可以通过实现 Runnable 接口并在其中实现

    2024年02月14日
    浏览(48)
  • 【昕宝爸爸小模块】浅谈之创建线程的几种方式

    ➡️博客首页       https://blog.csdn.net/Java_Yangxiaoyuan        欢迎优秀的你👍点赞、🗂️收藏、加❤️关注哦。        本文章CSDN首发,欢迎转载,要注明出处哦!        先感谢优秀的你能认真的看完本文,有问题欢迎评论区交流,都会认真回复! 在Java中,共有

    2024年01月18日
    浏览(57)
  • C++实现回调函数的几种方式

    通常认为通过函数指针在其他地方调用函数的过程称为回调,被调用的函数称为回调函数,函数指针通过传参方式传递。 在C++的实际应用中,有很多实现回调函数的方式,不一定要传递函数指针才能实现回调,只要实现了回调的功能,都叫回调函数。 回调函数是和调用者在

    2024年02月16日
    浏览(40)
  • Baumer工业相机堡盟工业相机使用BGAPI SDK将图像数据转换为Bitmap的几种方式(C++)(Mono)

    Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具有快速数据传输、低功耗、易于集成以及高度可扩

    2024年02月03日
    浏览(64)
  • C++ - stdmap正向遍历与反向遍历的几种方式

    文章目录 1 std::map正向遍历 1.1 for循环 1.2 while循环 2 std::map反向遍历 2.1 for循环 2.2 while循环 如果有兴趣可以访问我的个人站:https://www.stubbornhuang.com

    2024年02月12日
    浏览(50)
  • 外网连接局域网的几种方式?快解析内网穿透安全便利吗?

    外网连接局域网是一项网络连接中的关键技术,它能够让远程用户通过互联网访问内部局域网中的资源和服务。外网连接局域网为企业提供了更大的灵活性和便捷性,但也需要严格的安全措施来防止未经授权的访问。  外网连接局域网的几种方式 在将外网连接到局域网时,有

    2024年02月11日
    浏览(56)
  • Hive的几种排序方式、区别,使用场景

    Hive 支持两种主要的排序方式: ORDER BY 和 SORT BY 。除此之外,还有 DISTRIBUTE BY 和 CLUSTER BY 语句,它们也在排序和数据分布方面发挥作用。 1. ORDER BY ORDER BY 在 Hive 中用于对查询结果进行全局排序,确保结果集是全局有序的。但是,使用 ORDER BY 时,Hive 会将所有数据集中到一个

    2024年02月02日
    浏览(41)
  • Python Requests使用Cookie的几种方式

    这篇文章将总结 Python Requests库中接口请求时使用Cookie的几种方式,文章使用 Postman 官网提供的接口进行演示 https://postman-echo.com 通过headers参数使用cookie,key为Cookie,值为使用;拼接的 cookie_name=cookie_value 字符串 输出如下: 通过cookies参数使用cookie,cookies 值以字典形式提供,

    2024年02月13日
    浏览(51)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包