atomic和volatile的操作的原子性

C++ atomic 原子操作 在多线程编程中,通常我们需要多个线程共享数据。但如果不加以控制,多个线程同时访问同一数据,可能会导致数据不一致或竞争条件(race conditions)。为了解决这个问题,我们引入了。 阅读详情

       c++11提供了atomic来实现无锁并发数据结构,用起来很简单。

     

#include 
#include 
#include 
#include 

using namespace std;

//std::mutex mu_lock;
//int num = 0;
std::atomic num(0);

void testThread1()
{
    for (int i = 0;i < 1000;i++) {
//        lock_guard lock(mu_lock);
        num += 1;
    }
}

void testThread2()
{
    for (int i = 0;i < 1000;i++)
    {
//        lock_guard lock(mu_lock);
        num += 1;
    }
}

int main()
{
    std::thread thread1(testThread1);
    std::thread thread2(testThread2);

    thread1.join();
    thread2.join();
    std::cout << "num = " << num << std::endl;
}

     注释的部分是在没有原子操作支持情况下的并发设计(加锁),虽然可行但是在效率上没有atomic高。

     通常, 标准原子类型是不能拷贝和赋值,因赋值和拷贝调用了两个对象, 这就就破坏了操作的原子性。 在这样的情况下, 拷贝构造和拷贝赋值都会将第一个对象的值进行读取, 然后再写入另外一个。 对于两个独立的对象, 这里就有两个独立的操作了, 合并这两个操作必定是不原子的。 因此, 操作就不被允许。 但是, 因为可以隐式转化成对应的内置类型, 所以这些类型依旧支持赋值(注意这里是c++内置类型,而非自定义类型)。   

    另一件需要注意的事情时, atmoic_bool类型的赋值操作符不同于通常的操作,通常的赋值操作符返回对应类型的引用, 再赋给对应的对象。 但是在原子类型中返回对应类型的值而非引用, 这是一种常见的模式:赋值操作通过返回值(返回相关的非原子类型)完成, 而非返回引用。 如果一个原子变量的引用被返回了, 任何依赖与这个赋值结果的代码都需要显式加载这个值, 潜在的问题是, 结果可能会被另外的线程所修改。 通过使用返回非原子值进行赋值的方式, 你可以避免这些多余的加载过程, 并且得到的值就是实际存储的值。

   atomic的底层采用了cas的实现原理,CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下在旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。更详细的介绍:点击打开链接

   虽然atomic能保证变量的线程安全性,但是也不是任何情况下都可以的,看下面的例子:

   

//
//  main.cpp
//  Demo
//
//  Created by 杜国超 on 17/7/9.
//  Copyright © 2017年 杜国超. All rights reserved.
//

#include 
#include 
#include 
#include 
#include 

using namespace std;

std::atomic num(0);
volatile num1 = 0;

void testThread1()
{
    for (int i = 0;i < 1000;i++) {
        int temp = num;
        /////////////Do Something With temp,比如temp++/////////
        temp++;
        /////////////Do Something End/////////
        num = temp;
        num1++; 
    }
}

void testThread2()
{
    for (int i = 0;i < 1000;i++)
    {
        int temp = num;
        /////////////Do Something With temp,比如temp++/////////
        temp++;
        /////////////Do Something End/////////
        num = temp;
        num1++; 
    }
}

int main()
{
    list temp;
    std::thread thread1(testThread1);
    std::thread thread2(testThread2);

    thread1.join();
    thread2.join();
    std::cout << "num = " << num << std::endl;
    std::cout << "num1 = " << num1 << std::endl;
}

   上面的例子,不论是num还是num1我们是得不到正确的结果的,因为atmoic操作只能保证num=temp,这一句的原子性,而注释中间的部分atomic是无能为力的,我们经过了一系列的操作最终直接把结果付给了它,这样操作和赋值成为了两个部分,必定不是原子的。换句话说只有atomic模版本身已有的操作是原子的,只有它已重载的运算是原子的,我们必须使用这些操作来写变量,否则就要通过锁来实现了:

template 
struct __atomic_base<_Tp, true>
    : public __atomic_base<_Tp, false>
{
    typedef __atomic_base<_Tp, false> __base;
    _LIBCPP_INLINE_VISIBILITY
    __atomic_base() _NOEXCEPT _LIBCPP_DEFAULT
    _LIBCPP_INLINE_VISIBILITY
    _LIBCPP_CONSTEXPR __atomic_base(_Tp __d) _NOEXCEPT : __base(__d) {}
    
    //成员函数
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_add(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_add(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_sub(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_sub(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_and(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_and(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_or(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_or(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_xor(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_xor(&this->__a_, __op, __m);}

    //已重载的运算符
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++(int) volatile _NOEXCEPT      {return fetch_add(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++(int) _NOEXCEPT               {return fetch_add(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--(int) volatile _NOEXCEPT      {return fetch_sub(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--(int) _NOEXCEPT               {return fetch_sub(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++() volatile _NOEXCEPT         {return fetch_add(_Tp(1)) + _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++() _NOEXCEPT                  {return fetch_add(_Tp(1)) + _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--() volatile _NOEXCEPT         {return fetch_sub(_Tp(1)) - _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--() _NOEXCEPT                  {return fetch_sub(_Tp(1)) - _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator+=(_Tp __op) volatile _NOEXCEPT {return fetch_add(__op) + __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator+=(_Tp __op) _NOEXCEPT          {return fetch_add(__op) + __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator-=(_Tp __op) volatile _NOEXCEPT {return fetch_sub(__op) - __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator-=(_Tp __op) _NOEXCEPT          {return fetch_sub(__op) - __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator&=(_Tp __op) volatile _NOEXCEPT {return fetch_and(__op) & __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator&=(_Tp __op) _NOEXCEPT          {return fetch_and(__op) & __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator|=(_Tp __op) volatile _NOEXCEPT {return fetch_or(__op) | __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator|=(_Tp __op) _NOEXCEPT          {return fetch_or(__op) | __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator^=(_Tp __op) volatile _NOEXCEPT {return fetch_xor(__op) ^ __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator^=(_Tp __op) _NOEXCEPT          {return fetch_xor(__op) ^ __op;}
};
struct __atomic_base<_Tp, true>
    : public __atomic_base<_Tp, false>
{
    typedef __atomic_base<_Tp, false> __base;
    _LIBCPP_INLINE_VISIBILITY
    __atomic_base() _NOEXCEPT _LIBCPP_DEFAULT
    _LIBCPP_INLINE_VISIBILITY
    _LIBCPP_CONSTEXPR __atomic_base(_Tp __d) _NOEXCEPT : __base(__d) {}
    
    //成员函数
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_add(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_add(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_sub(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_sub(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_and(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_and(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_and(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_or(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_or(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_or(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
        {return __c11_atomic_fetch_xor(&this->__a_, __op, __m);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
        {return __c11_atomic_fetch_xor(&this->__a_, __op, __m);}

    //已重载的运算符
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++(int) volatile _NOEXCEPT      {return fetch_add(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++(int) _NOEXCEPT               {return fetch_add(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--(int) volatile _NOEXCEPT      {return fetch_sub(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--(int) _NOEXCEPT               {return fetch_sub(_Tp(1));}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++() volatile _NOEXCEPT         {return fetch_add(_Tp(1)) + _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator++() _NOEXCEPT                  {return fetch_add(_Tp(1)) + _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--() volatile _NOEXCEPT         {return fetch_sub(_Tp(1)) - _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator--() _NOEXCEPT                  {return fetch_sub(_Tp(1)) - _Tp(1);}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator+=(_Tp __op) volatile _NOEXCEPT {return fetch_add(__op) + __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator+=(_Tp __op) _NOEXCEPT          {return fetch_add(__op) + __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator-=(_Tp __op) volatile _NOEXCEPT {return fetch_sub(__op) - __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator-=(_Tp __op) _NOEXCEPT          {return fetch_sub(__op) - __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator&=(_Tp __op) volatile _NOEXCEPT {return fetch_and(__op) & __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator&=(_Tp __op) _NOEXCEPT          {return fetch_and(__op) & __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator|=(_Tp __op) volatile _NOEXCEPT {return fetch_or(__op) | __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator|=(_Tp __op) _NOEXCEPT          {return fetch_or(__op) | __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator^=(_Tp __op) volatile _NOEXCEPT {return fetch_xor(__op) ^ __op;}
    _LIBCPP_INLINE_VISIBILITY
    _Tp operator^=(_Tp __op) _NOEXCEPT          {return fetch_xor(__op) ^ __op;}
};

    volatile同样的道理,num1++语句设计2个操作,一个是读出num1的值,另外一个是写入num1++后的新值,volatile只能保证2个线程之前写入的值对于另外一个线程的可见性:点击打开链接,防止伪共享的产生:点击打开链接,并但是不能保证先读后写这两个操作的原子性,更不能保证代码块的原子性(atomic也不能保证这点),所以当我们仅仅是get和set时可以用volatile来保证数据线程安全,但是如果get后有操作才set那么此时就要用volatile来保证了,如果你需要保证volatile变量和其他无关的读写顺序不被cpu乱序执行,那么在多核环境下volatile没有任何作用,它仅仅保证编译器级别的乱序保证,所以。

   1) 与平台无关的多线程程序,volatile几乎无用(Java的volatile除外);
   2) volatile不保证原子性(一般需使用CPU提供的LOCK指令);
   3) volatile不保证执行顺序;
   4) volatile不提供内存屏障(Memory Barrier)和内存栅栏(Memory Fence)揭开内存屏障的面纱;
   5) 多核环境中内存的可见性和CPU执行顺序不能通过volatile来保障,而是依赖于CPU内存屏障。
   注:volatile诞生于单CPU核心时代,为保持兼容,一直只是针对编译器的,对CPU无影响。

      注意,原因1中把java排除了,因为不同编程语言中voldatile含义与实现并不完全相同,Java语言中voldatile变量可以被看作是一种轻量级的同步,因其还附带了acuire和release语义。实际上也是从JDK5以后才通过这个措施进行完善,其volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。Java语言中有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个lock指令,就是增加一个完全的内存屏障指令,这点与C++实现并不一样,volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。
Java实践中仅满足下面这些条件才应该使用volatile关键字:
    1) 变量写入操作不依赖变量当前值,或确保只有一个线程更新变量的值(Java可以,C++仍然不能)
    2)该变量不会与其他变量一起纳入
    3) 变量并未被锁保护
    C++中voldatile等于插入编译器级别屏障,因此并不能阻止CPU硬件级别导致的重排。C++11 中volatile语义没有任何变化,不过提供了std::atomic工具可以真正实现原子操作,而且默认加入了内存屏障(可以通过在store与load操作时设置内存模型参数进行调整,默认为std::memory_order_seq_cst)C++实践中推荐涉及并发问题都使用std::atomic,只有涉及特殊内存操作的时候才使用volatile关键字。这些情况通常IO相关,防止相关操作被编译器优化,也是volatile关键字发明的本意。

    最后再说一下关于自定义类型的atomic,atomic是一个模版,我们可以实现自定义的原子类型,但是是有条件的。为了使用 std::atomic<UDT> (UDT是用户定义类型), 这个类型必须有拷贝赋值运算符。 这就意味着这个类型不能有任何虚函数或虚基类, 以及必须使用编译器创建的拷贝赋值操作。 不仅仅是这些, 自定义类型中所有的基类和非静态数据成员也都需要支持拷贝赋值操作。 这(基本上)就允许编译器使用memcpy(), 或赋值操作的等价操作, 因为它们的实现中没有用户代码。这个类型必须是“位可比的”(bitwise equality comparable)。 这与对赋值的要求差不多;你不仅需要确定, 一个UDT类型对象可以使用memcpy()进行拷贝, 还要确定其对象可以使用memcmp()对位进行比较。 之所以要求这么多, 是为了保证“比较/交换”操作能正常的工作。

   不要将锁定区域内的数据, 以引用或指针的形式, 作为参数传递给用户提供的函数。 通常情况下, 编译器不会为 std::atomic<UDT> 类型生成无锁代码, 所以它将对所有操作使用一个内部锁。 如果用户提供的拷贝赋值或比较操作被允许, 那么这就需要传递保护数据的引用作为一个参数, 这就有悖于指导意见了。 当原子操作需要时, 运行库也可自由的使用单锁, 并且运行库允许用户提供函数持有锁, 这样就有可能产生死锁(或因为做一个比较操作, 而组设了其他的线程)。 最终, 因为这些限制可以让编译器将用户定义的类型看作为一组原始字节, 所以编译器可以对 std::atomic<UDT> 直接使用原子指令(因此实例化一个特殊无锁结构)

  关于这一点也是参考别人的,个人感觉就是几点:

   1:必须有拷贝赋值运算符,必须使用编译器创建的拷贝赋值操作(不是很理解这点)

   2:不能有虚函数和需基类

   3:成员函数中不能以指针或者引用的形式做参数,活着做返回值

   4:位可比性

 

   

C++中atomic的使用 本文只要讲述C++11中atomic的使用,并通过示例来进一步的说明。atomic在c++11中被引入,作为一种原子操作,之所以是原子操作,是因为其能保证操作过程不会被打断或终止,保证了被操作数据的安全性。原子操作是线程安全的。尤其在多线程同步中,常用于基本类型共享数据的保护。原子操作:指的是不可被中断的一个或一系列操作,这些操作要么全部执行成功,要么全部不执行。 阅读详情

相关推荐

Java并发编程:Volatile关键字和Atomic类

在接触并发编程之前我对volatile关键字是没有什么映像的,这个关键字解决了什么问题呢?让我们先来看一个示例: public class UseVolatitle extends Thread { private boolean isrunning = true; public void setIsrunning(boolean isrunning) { thi...

张翠山的博客 3185

C++ 标准库 <atomic>

atomic>提供了原子操作类型和函数,使多个线程在不加锁的情况下也能安全地访问和修改共享数据。通过硬件或编译器内置的原子指令实现,通常具有比互斥量更低的开销,但对内存可见性和指令重排更敏感。原子类型原子操作内存序(memory_order),以及屏障。

m0_57545130的博客 1769

Java并发编程的瑞士军刀:掌握原子性操作

在Java的世界中,多线程编程就像一场精心编排的交响乐,每一个线程都是乐团中的乐手,协同合作演奏出和谐的旋律。然而,当多个线程需要访问共享资源时,如果没有恰当的指挥,就可能引发数据不一致的问题,这时就需要原子性操作这位“指挥家”来确保每个操作都能完整无误地执行。今天,让我们深入探索Java中保证原子性的那些精妙方法。

u012808915的博客 633

volatile与Atomic的比较

一、volatile的作用 我们已经知道可见性、有序性及原子性问题,通常情况下我们可以通过Synchronized关键字来解决这些个问题,不过如果对Synchronized原理有了解的话,应该知道Synchronized是一个比较重量级的操作,对系统的性能有比较大的影响,所以,如果有其他解决方案,我们通常都避免使用Synchronized来解决问题。 而volatile关键字就是Java中提供的另一种解决可见性和有序性问题的方案。对于原子性,需要强调一点,也是大家容易误解的一点:对volatile变量的

飞奔的小强 7764

【超详解】C++原子变量atomic,全面解密!

原子类型的对象包含特定type ()的值。原子对象的主要特征是,从不同的线程访问这个包含的值不会导致数据竞争(即,这样做是明确定义的行为,访问正确排序)。通常,对于所有其他对象,导致同时访问同一对象的数据争用的可能性将操作限定为未定义行为。【官方介绍】。此外,对象能够通过指定不同的内存顺序来同步对其线程中其他非原子对象的访问。原子操作在多线程中可以保证线程安全,而且效率会比互斥量好些。

Lion_Long的博客 1万+

【驭风计划 机器学习】实验6 基于集成学习的 Amazon 用户评论质量预测

¶随着电商平台的兴起,以及疫情的持续影响,线上购物在我们的日常生活中扮演着越来越重要的角色。在进行线上商品挑选时,评论往往是我们十分关注的一个方面。然而目前电商网站的评论质量参差不齐,甚至有水军刷好评或者恶意差评的情况出现,严重影响了顾客的购物体验。因此,对于评论质量的预测成为电商平台越来越关注的话题,如果能自动对评论质量进行评估,就能根据预测结果避免展现低质量的评论。本案例中我们将基于集成学习的方法对 Amazon 现实场景中的评论质量进行预测。

Mege1的博客 471

深入理解Atomic原子操作和volatile非原子性

首先,我们要理解什么叫原子操作,原子操作可以理解为:在多线程操作同一对象时,在非人为程序加锁状况下,保证被操作对象是线程安全的。 翻译为人话就是:一个数,很多线程去同时修改它,不用sync加锁,就可以保证修改结果是正确的。 那这是如何保证的呢?我们先了解一下下面这个东西: CAS(Compare and swap):比较和替换是设计并发算法时用到的一种技术。简单来说,比较和替换是使用一个期望

王小码正是在下 7065

2025 年第十五届 APMCM 亚太地区大学生数学建模竞赛A题 农业灌溉系统优化

该摘要探讨了农业灌溉系统优化面临的复杂挑战,涉及智能灌溉技术的应用与资源配置问题。研究聚焦于一个1公顷的方形农场案例,该农场种植三种作物并面临动态环境条件(土壤类型、气象变化)。系统优化需平衡河流引水(非线性成本函数:C=50L^1.2+0.1Q^1.5)与储水罐灌溉(成本5元/升容积)两种方案,同时满足喷灌技术约束(喷头间距≥15米,覆盖半径15米)。关键矛盾体现在"平河旱储"的水资源调配策略与多作物需水量差异之间的协调,需通过传感器数据实时调整灌溉方案以实现效率最大化。(149字)

m0_60472361的博客 641

Volatile vs Atomic

volatile和atomic这两个关键字经常会出现在我们的视线中。它们虽然看似相似,实则功能大不相同。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 6108

别再问“这游戏啥引擎做的”了,手把手教你用SteamDB一键查询(附实战案例)

本文详细介绍了如何使用SteamDB一键查询游戏引擎信息,包括Unity 3D、Unreal Engine等热门引擎的识别方法。通过实战案例和高级查询技巧,帮助玩家快速获取游戏技术底细,优化游戏体验和兼容性分析。

weixin_31412781的博客 815

深入解析Cuk、Zeta和Sepic拓扑:非隔离型开关电源的设计奥秘

本文深入解析了Cuk、Zeta和Sepic三种非隔离型开关电源拓扑的设计奥秘。它们均能实现升降压转换,但特性各异:Cuk拓扑输出反向电压,输入输出纹波极低;Sepic拓扑输出同极性电压,应用广泛;Zeta拓扑输出纹波小,适合精密供电。文章详细对比了其电路结构、工作原理、核心公式及选型指南,帮助工程师根据输入电压范围、输出极性及纹波要求,灵活选择最合适的拓扑方案。

weixin_28308325的博客 474

java中volatile 变量是什么?volatile 变量和atomic 变量有什么不同?

java中volatile 变量是什么?volatile 变量和atomic 变量有什么不同?

java1234的博客 1154

Python在Windows环境下驱动乐视三合一摄像头的深度图像处理实践

本文详细介绍了在Windows系统下,使用Python驱动乐视三合一摄像头进行深度图像处理的完整实践流程。从环境配置、驱动安装、OpenNI与OpenCV库的部署,到深度与彩色数据流的读取、可视化、三维点云转换以及背景移除等应用开发,为开发者提供了从零开始掌握深度摄像头编程的实战指南。

gitlab7runner的博客 231

什么是原子性?volatile能保证原子性吗?

作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。

smart_an的专栏 810

OBC_开环扫频波特图拟合操作流程(结合 Simulink)

摘要: 本文详细介绍了基于Simulink的开环扫频波特图测试与拟合流程,适用于电源/控制系统(如LLC/PFC)的小信号分析。主要内容包括:1)前期准备(测试点选择、信号范围设定);2)Simulink建模方法(信号注入、数据记录);3)两种扫频方式(逐频正弦/Chirp扫频)的实现;4)MATLAB数据处理(FFT分析、传递函数拟合);5)线性分析工具使用技巧。重点强调了激励信号幅值控制、稳态数据采集及模型验证方法,并提供完整MATLAB脚本示例。该流程可有效获取系统频域特性,为控制器设计提供可靠依据。

Sumerking的博客 232

《多模态融合改进》支持目标检测、目标分割、OBB、姿态估计,配置教程

本文以为例,讲解如何配置,支持替换专栏里所有的改进方式。

Limiiiing的博客 795

volatile 变量和 atomic 变量

volatile和Atomic变量是 Java 并发编程中的两种不同同步机制,分别解决不同场景下的并发问题。volatile关键字仅保证共享变量的可见性,适用于简单的状态标识和轻量级读写场景,而Atomic变量则使用 CAS 操作来保证原子性,适用于更复杂的多线程环境和高并发场景。选择volatile还是Atomic变量应根据具体的需求来决定。对于简单的共享变量状态更新和标识控制,volatile是首选;而在涉及复合操作(如递增、递减等)和需要更高性能和原子性的场景中,Atomic变量无疑是更好的选择。

Flying_Fish_roe的博客 1302

12-RK平台U-Boot启动流程

先看概述,把这一章要回答的问题摆出来,带着问题听。RK 特有的 BootROM -> MiniLoader -> SPL -> U-Boot -> Kernel。这是因为 RK 平台使用闭源的 DDR 初始化固件,需要通过 MiniLoader 来加载和运行。为什么多一层?这是 RK 平台的特殊性决定的。RK 平台使用闭源的 DDR 初始化固件,这段代码 Rockchip 不开源,只能以二进制形式提供,它需要一个载体来加载和运行,这个载体就是 MiniLoader。

内核笔记 1146

volatile 变量和 atomic 变量有什么不同?

volatile 变量:使用 volatile 关键字修饰的变量可以确保对该变量的读取和写入操作对其他线程是可见的。volatile变量:适用于对变量的读取和写入操作都是简单的赋值操作,并且需要保证对其他线程的可见性。volatile变量:volatile 关键字只能保证对单个变量的读取和写入操作的可见性,不能保证复合操作的原子性。Atomic变量提供了一组原子操作方法,可以保证对变量的操作是原子的,同时也具有可见性。volatile 变量保证了对变量的读取和写入操作的可见性,但不能保证复合操作的原子性。

weixin_73855020的博客 882

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

[多线程]为什么volatile不能保证原子性而Atomic可以?

在网上看到了这篇文章,下面我给大家总结一下文章大概的意思 volatile可以在多线程的情况下,保证当值修改后会被其他的线程读取到最新的值,这得益于cpu的(内存屏障指令) 看了一下这个内存屏障指令,大概的意思是说当内存屏障指令发生后,cpu和编译器会收到这个命令,告诉cpu和编译器必须先于某条命令执行,后于某条指令执行,并且强制更新一次cpu缓存,这样其他线程就会拿到最新的值。 ...

没有腹肌的码农不是好的歌神的博客 733

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

为何volatile不能保证原子性操作

被volatile修饰的变量可以保证该变量的可见性,但并不能保证操作的原子性。 当需要使用被volatile修饰的变量时,线程会从主内存中重新获取该变量的值,但当该线程修改完该变量的值写入主内存的时候,并没有判断主内存内该变量是否已经变化,故可能出现非预期的结果。如主内存内有被volatile修饰变量 a,值为3,某线程使用该变量时,重新从主存内读取该变量的值,为3,然后对其进行+1操作,此时该线...

qq_36382225的博客 823

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

volatile和Atomic

volatile 简单来说,volatile保存内存可见性。 线程一和二从主内存中复制flag到自己的工作内存。 当线程一修改flag的值,线程二并不会立即从主内存中读取flag的值,所以在线程二的工作内存中,flag依然为false。 使用volatile关键字,告示JVM此变量是不稳定的,每次使用它都要到主内存中读取。 除了保证内存可见性,volatile还能防止指令重排序,但并不能保证原...

dzyls的笔记 1601

Java里面的atomic类是如何保证原子性的

Java中的Atomic类通过几种机制共同协作来保证原子性,这些机制主要包括CAS(Compare And Swap)操作、volatile关键字以及底层的本地方法。

xuweilin的博客 864
上一篇: golang同步之sync包
下一篇: golang 如何将多路复异步io转变成阻塞io的
D_Guco
博客等级 码龄12年 344粉丝 111原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值