程序员应了解的那些事(112)~原子操作

一、C语言中的原子操作

什么是原子操作?

        原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断,可以视为最小的操作单元,因此称为原子操作(个人认为叫"原子级操作更为形象")。
       (可以是一条指令,也可以是一系列整体性操作)

在编程语言中一般分为两种情况(两种都应该满足):

1) 在单线程中, 能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。(多体现在单条汇编指令语句中)

2) 在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。

为什么存在原子操作?

        在多核处理器的机器上,对于同一个变量值所存在的内存区域可能被多个CPU短时间内同时访问,从而导致该值得不稳定性。例如在多线程中,初始化i=0,对于i++这个操作,同时有2个线程对其进行了调用则有可能导致第一个线程用时i的值为2。(详解如下)

i++是否为原子操作?

①内存访问,读取i变量的值到CPU的寄存器中
②使寄存器中的值+1
③将寄存器中的值写回到内存

        由此可见,i++并不是原子操作,在没有线程保护的多线程处理器中,每一步都有可能被外部中断给打断,从而影响到 i 的值。

        在上一问中,2个线程同时调用了i++,i直接从0变为2,则极有可能是线程1在寄存器中使得i自增1的同时线程2也在做此操作,因此当取出寄存器中i的值到内存时,i 不知不觉被加了2次。

二、什么叫原子操作?使用这个东西有什么目的?

(1)我们要先搞明白什么叫原子操作?使用这个东西有什么目的?


原子操作:能够操作最接近机器的指令,这个和硬件相关了,虽然和硬件相关,但我们的C11还是整合了这一切,让原子操作有了共同的调用接口。
目的:使用这个的目的说实话,就是让你更了解机器已及多线程同步的原理和秘密,当然有一些需求较简单的,使用原子操作可能比封装好的更有效率!!用了百遍的mutex可能你现在还不知道他们是怎么互斥的~当然内部还是通过了原子操作来的!

(2)讲讲原理
        原子操作只有2种状态,一种是没做,一种是做完了,看不到正在做的状态,这个是在任何线程下都满足这个要求,当两个线程同时访问一块内存的时候,如果有任何一个在写,那肯定会产生竞争,如果两个同时读,没有问题,那如何用原子操作来控制不产生竞争呢?可以这样来想,当两个线程在访问的时候,一定有一个先后顺序,谁先访问,谁后访问,这就是修改顺序,我们要在任何线程可以看到这样的顺序!然后就可以通过一定的逻辑来处理并发竞争的情况了!

(3)标准库操作
例:实现一个线程读取,一个线程写入,当然,要先写入才能读取,所以这个是顺序问题。

方法一:condition_variable来操作

使用condition_variable,读线程wait,直到写线程调用 notify_all,即停止等待:

#include <thread>
#include <condition_variable>
#include <iostream>
using namespace std;
 
condition_variable g_CV;
mutex g_mtx;
void read_thread()
{
    while (true)
    {
        unique_lock<mutex> ul(g_mtx);
        g_CV.wait(ul);
        cout << g_value << endl;
    }
}
void write_thread()
{
    while (true)
    {
        Sleep(1000);
        lock_guard<mutex> lg(g_mtx);
        g_value++;
        g_CV.notify_all();
    }
}
int main()
{
    thread th(read_thread);
    th.detach();
    thread th2(write_thread);
    th2.detach();
    char a;
    while (cin >> a);
    return 0;
}

方法二:使用标准原子操作

#include <thread>
#include <atomic>
#include <iostream>
using namespace std;
 
int g_value(0);
atomic<bool> g_data_ready(false);
 
void read_thread()
{
    while (true)
    {
        while (!g_data_ready.load());//用于控制进入
        cout << g_value << endl;
        g_data_ready = false;
    }
}
 
void write_thread()
{
    while (true)
    {
        while (g_data_ready.load());//用于控制进入
        Sleep(1000);
        g_value++;
        g_data_ready = true;
    }
}
 
int main()
{
    thread th(read_thread);
    th.detach(); //detach表示该线程可以在后台运行,无需等待当前线程完成,会继续执行后面的操作
    thread th2(write_thread);
    th2.detach();
    char a;
    while (cin >> a);
    return 0;
}

参考文档(拓展提升):

什么叫原子操作?使用这个东西有什么目的?_aFakeProgramer的博客-CSDN博客_原子操作的作用

原子操作(atomic operation)_远有青山的博客-CSDN博客_atomic operation

猜你喜欢

转载自blog.csdn.net/qfturauyls/article/details/128411323
今日推荐