C++多线程,线程安全管理

C++多线程,线程安全管理

多线程同时访问一个内存地址,进行读写,

可能会出现以下四种情况:(瞎猜的,不具备任何经验时的猜测)

1、软件卡死(死锁)

2、软件闪退,非法访问内存空间,代码崩溃;

3、程序正常结束,但是算出来的值有问题;

4、程序正常结束,算出来的值也OK;


测试结论:程序正常结束,但是算出来的值有问题;

1、print_thread_id函数,对同一个内存地址进行读写

2、外层开i_thread_num个线程;

#include <stdio.h>
#include <iostream>
#include <boost/thread.hpp>

// extern "C"
// void useCUDA();



// 线程执行的函数
void print_thread_id(int * var) {

	boost::thread::id this_id = boost::this_thread::get_id();

	for(int i = 0; i < 100000; i++)
	{
		var[0]++;
	}

	// std::cout << "Thread ID: " << this_id << ", addr=" << &var[0] << ", value=" << var[0] << std::endl;
}


int main()
{
	std::cout<<"Hello C++"<<std::endl;

	int a = 0;
	std::cout << "addr=" << &a << std::endl;
	int i_thread_num = 10;

	boost::thread_group threadArray;
	for(int i_thread_idx = 0; i_thread_idx < i_thread_num; i_thread_idx++)
	{
		threadArray.create_thread(boost::bind(&print_thread_id, &a));

	}
	threadArray.join_all();


	std::cout << "Final value=" << a <<std::endl;

	// useCUDA();
	return 0;
}

实验1,print_thread_id 内层循环100000

程序的输出应该是:100000 * 10

实际结果:

addr=0000000000DEFE30
Final value=271702

结论:多线程读写,不安全

实验2:print_thread_id 内层循环10000

程序的输出应该是:10000 * 10

实际结果:

addr=00000000003EFA90
Final value=96754

结论:多线程读写,不安全

实验3:print_thread_id 内层循环1000

程序的输出应该是:1000 * 10

实际结果(第一次):

addr=000000000038FA90
Final value=9336

实际结果(第二次):

addr=0000000000CFF710
Final value=10000

结论:多线程读写,不安全

实验4:print_thread_id 内层循环100

程序的输出应该是:100 * 10

实际结果:(第一次):

addr=0000000000BCFEF0
Final value=1000

实际结果:(第二次):

addr=00000000008FFC70
Final value=1000

实际结果:(第三次):

addr=0000000000AFF7C0
Final value=1000

结论:在读写次数比较少的时候,异常结果出现的可能性也比较小。但是从设计原理上讲,不推荐多线程对同一个内存空间,进行读写。

C++ 多线程同时读写同一个内存地址时,如何保证线程安全?

在C++中,当多个线程同时读写同一个内存地址时,必须采取措施来保证线程安全,以避免数据竞争、条件竞争等问题,这些问题可能导致未定义行为、数据损坏或程序崩溃。以下是一些常用的方法来保证线程安全:

  1. 互斥锁(Mutex)
    互斥锁是最常用的同步机制之一。它可以确保同一时刻只有一个线程可以访问被保护的代码段(临界区)。当线程进入临界区前,必须先获取互斥锁;离开临界区时,释放互斥锁。

    #include <mutex>
    std::mutex mtx;
    
    void safe_function() {
        std::lock_guard<std::mutex> lock(mtx);
        // 临界区:访问共享资源
    }
    

    std::lock_guard 是一个RAII(Resource Acquisition Is Initialization)风格的锁管理器,它在构造时自动加锁,在析构时自动解锁,非常方便。

  2. 读写锁(Reader-Writer Locks)
    如果共享数据被读取的频率远高于写入的频率,使用读写锁可以提高性能。读写锁允许多个线程同时读取数据,但写入时必须独占访问。

    #include <shared_mutex>
    std::shared_mutex rw_mtx;
    
    void read_function() {
        std::shared_lock<std::shared_mutex> lock(rw_mtx);
        // 读取共享资源
    }
    
    void write_function() {
        std::unique_lock<std::shared_mutex> lock(rw_mtx);
        // 写入共享资源
    }
    
  3. 原子操作(Atomic Operations)
    对于简单的数据类型(如整数、指针等),可以使用C++11引入的<atomic>库中的原子类型来保证操作的原子性。原子操作不可分割,执行过程中不会被线程调度机制中断。

    #include <atomic>
    std::atomic<int> count = 0;
    
    void increment() {
        ++count; // 原子操作
    }
    
  4. 条件变量(Condition Variables)
    虽然条件变量本身不直接用于保护共享数据,但它通常与互斥锁一起使用,用于实现线程间的同步,特别是在等待某个条件成立时。

    #include <mutex>
    #include <condition_variable>
    
    std::mutex mtx;
    std::condition_variable cv;
    bool ready = false;
    
    void wait_for_ready() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{return ready;});
        // 执行后续操作
    }
    
    void set_ready() {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        cv.notify_one(); // 或 notify_all()
    }
    
  5. 无锁编程(Lock-free Programming)
    无锁编程是一种高级技术,通过原子操作和特定的算法设计来避免使用锁,从而提高性能。但是,它实现起来较为复杂,且容易出错,需要深入理解并发编程的原理。

每种方法都有其适用场景,通常需要根据具体的应用场景和性能要求来选择最合适的方法。在实际开发中,应尽量避免在多个线程间共享数据,或尽量将共享数据的访问限制在最小范围内,以降低并发编程的复杂性和出错率。


https://blog.csdn.net/XiaoH0_0/article/details/97411942


https://blog.csdn.net/XiaoH0_0/article/details/97411942

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值