您的位置:首页 > 财经 > 产业 > 面试二十七、 CAS和Atomic

面试二十七、 CAS和Atomic

2024/10/5 20:22:45 来源:https://blog.csdn.net/leimeili/article/details/139302104  浏览:    关键词:面试二十七、 CAS和Atomic

CAS锁机制(无锁、自旋锁、乐观锁、轻量级锁)-CSDN博客

1. ABA问题

在C++中,可以使用std::atomic和版本号来解决ABA问题。C++标准库没有直接提供类似Java的AtomicStampedReference,但可以通过将版本号和指针组合在一起实现类似的效果。

#include <atomic>
#include <iostream>
#include <thread>template <typename T>
class AtomicStampedReference {
public:AtomicStampedReference(T initial_value, int initial_stamp): value_(initial_value), stamp_(initial_stamp) {}bool compare_and_set(T expected_value, T new_value, int expected_stamp, int new_stamp) {auto current_value = value_.load();auto current_stamp = stamp_.load();return (current_value == expected_value && current_stamp == expected_stamp) &&value_.compare_exchange_strong(current_value, new_value) &&stamp_.compare_exchange_strong(current_stamp, new_stamp);}T get_reference() const {return value_.load();}int get_stamp() const {return stamp_.load();}private:std::atomic<T> value_;std::atomic<int> stamp_;
};void aba_example() {AtomicStampedReference<int> atomicStampedRef(100, 0);auto thread1 = [&atomicStampedRef]() {int stamp = atomicStampedRef.get_stamp();int reference = atomicStampedRef.get_reference();std::cout << "Thread 1 initial stamp: " << stamp << "\n";std::cout << "Thread 1 initial value: " << reference << "\n";if (atomicStampedRef.compare_and_set(reference, reference + 1, stamp, stamp + 1)) {std::cout << "Thread 1 new stamp: " << atomicStampedRef.get_stamp() << "\n";std::cout << "Thread 1 new value: " << atomicStampedRef.get_reference() << "\n";}};auto thread2 = [&atomicStampedRef]() {int stamp = atomicStampedRef.get_stamp();int reference = atomicStampedRef.get_reference();atomicStampedRef.compare_and_set(reference, reference + 1, stamp, stamp + 1);atomicStampedRef.compare_and_set(reference + 1, reference, stamp + 1, stamp + 2);std::cout << "Thread 2 stamp after ABA: " << atomicStampedRef.get_stamp() << "\n";std::cout << "Thread 2 value after ABA: " << atomicStampedRef.get_reference() << "\n";};std::thread t1(thread1);std::thread t2(thread2);t1.join();t2.join();
}int main() {aba_example();return 0;
}

2. 自旋时间长开销大

循环时间长开销大

  • 描述:当多个线程竞争同一个CAS操作时,如果一直失败,线程会不断自旋重试,造成CPU资源的浪费。尤其在高并发情况下,频繁的CAS重试会导致较高的CPU开销。
  • 解决办法:可以使用自适应自旋锁或其他锁机制来减轻自旋的开销。

        自旋锁在高竞争情况下会导致大量CPU资源浪费。可以使用自适应自旋锁或结合其他锁机制来减轻这种开销。以下是一个简单的自适应自旋锁实现示例。

#include <atomic>
#include <thread>
#include <iostream>
#include <chrono>class AdaptiveSpinLock {
public:AdaptiveSpinLock() : flag(ATOMIC_FLAG_INIT) {}void lock() {int spin_count = 0;while (flag.test_and_set(std::memory_order_acquire)) {++spin_count;if (spin_count > max_spin_count) {std::this_thread::yield(); // 出让CPUspin_count = 0;}}}void unlock() {flag.clear(std::memory_order_release);}private:static const int max_spin_count = 1000; // 自适应阈值std::atomic_flag flag;
};void spinlock_example() {AdaptiveSpinLock lock;int counter = 0;auto increment = [&lock, &counter]() {for (int i = 0; i < 1000; ++i) {lock.lock();++counter;lock.unlock();}};std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final counter value: " << counter << "\n";
}int main() {spinlock_example();return 0;
}

3. 多变量原子操作

CAS只能保证单个变量的原子操作,要保证多个变量的原子操作,可以使用锁机制。以下是使用std::mutex来保证多个变量的原子操作的示例。

#include <mutex>
#include <thread>
#include <iostream>class MultiVariable {
public:void update(int a, int b) {std::lock_guard<std::mutex> lock(mtx);var1 = a;var2 = b;}void get(int& a, int& b) {std::lock_guard<std::mutex> lock(mtx);a = var1;b = var2;}private:int var1 = 0;int var2 = 0;std::mutex mtx;
};void multivariable_example() {MultiVariable mv;auto writer = [&mv]() {for (int i = 0; i < 1000; ++i) {mv.update(i, i * 2);}};auto reader = [&mv]() {for (int i = 0; i < 1000; ++i) {int a, b;mv.get(a, b);std::cout << "Read values: " << a << ", " << b << "\n";}};std::thread t1(writer);std::thread t2(reader);t1.join();t2.join();
}int main() {multivariable_example();return 0;
}

4.CAS使用注意事项
(1)CAS需要和volatile配合使用

CAS只能保证变量的原子性,不能保证变量的内存可见性。CAS获取共享变量的值时,需要和volatile配合使用,来保证共享变量的可见性

(2)CAS适用于并发量不高、多核CPU的情况

CPU多核情况下可以同时执行,如果不合适就失败。而并发量过高,会导致自旋重试耗费大量的CPU资源。

5. volatile

  • C++中的volatile:主要用于防止编译器优化,确保每次访问变量时从内存中读取最新的值,但不保证多线程环境下的内存可见性和顺序一致性。
  • 内存可见性:在C++中使用std::atomic和内存序列来确保多线程环境下的内存可见性和顺序一致性。

5.atomic

【超详解】C++原子变量atomic,全面解密!_c++ atomic-CSDN博客

atomic的底层实现 - 王的博客 - 博客园 (cnblogs.com)

CPU多核同步原语 - 知乎 (zhihu.com)

深入解析现代C++中的原子(std::atomic)-51CTO.COM

std::atomic通过硬件提供的原子指令实现无锁的原子操作,确保在多线程环境下的数据一致性和线程安全。通过使用内存顺序,可以进一步控制操作的可见性和顺序,以满足不同的并发编程需求。

 

 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com