美文网首页
C语言原子操作

C语言原子操作

作者: 付凯强 | 来源:发表于2023-01-29 11:44 被阅读0次

什么是原子操作

  1. 原子操作指的是对原子对象的读和写是不可被打断的。一个操作不可被打断意味着在执行整个操作过程中,即便有一个硬件中断信号过来,该中断信号也不能立即触发处理器的中断执行例程,处理器必须执行完整条原子操作之后才可进入中断执行例程。
  2. 对于来自多个处理器核心对同一个存储空间的访问,存储器控制器会去仲裁当前哪个原子操作先进行访存操作,哪个后进行,这些访存操作都会被串行化。

所以原子对象往往用于多核多线程并行计算中对多个线程共享变量的计算。

什么是原子类型

用_Atomic(类型名)这种方式修饰的类型是原子类型,在实际使用原子类型时应当避免直接使用_Atomic(类型名)这种形式,而是直接用<stdatomic.h>头文件中已经定义好的原子类型。此外该头文件还有相应的原子操作函数。

常用的原子类型

typedef _Atomic _Bool atomic_bool;
typedef _Atomic char atomic_char;
typedef _Atomic signed char atomic_schar;
typedef _Atomic unsigned char atomic_uchar;
typedef _Atomic short atomic_short;
typedef _Atomic unsigned short atomic_ushort;
typedef _Atomic int atomic_int;
typedef _Atomic unsigned int atomic_uint;
typedef _Atomic long atomic_long;
typedef _Atomic unsigned long atomic_ulong;
typedef _Atomic long long atomic_llong;
typedef _Atomic unsigned long long atomic_ullong;
...

常用的原子操作函数

atomic_init 初始化
atomic_store 赋值
atomic_load 获取
atomic_fetch_add 加
atomic_fetch_sub 减
...

单线程示例

#include <stdio.h>
#include <stdatomic.h>

atomic_int atomic_count = ATOMIC_VAR_INIT(1);

void test()
{
    atomic_int a;
    atomic_init(&a,10);
    int* a_ptr = (int *) &a;
    atomic_store(a_ptr,20);
    int b = atomic_load(a_ptr);
    printf("b = %d\n",b);
    int* atomic_count_ptr = (int *) &atomic_count;
    int atomic_count_old_value = atomic_fetch_add(atomic_count_ptr,b);
    printf("atomic_count_old_value = %d\n",atomic_count_old_value);
    int int_atomic_count = atomic_load(atomic_count_ptr);
    printf("atomic_count = %d\n",int_atomic_count);
}

int main(void)
{
    test();
    return 0;
}

多线程示例

#include <stdio.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>

static volatile atomic_ullong sAtomResult = ATOMIC_VAR_INIT(0);
static volatile atomic_int sAtomIndex = ATOMIC_VAR_INIT(0);
static volatile uint64_t sNormalResult = 0;
static volatile int sNormalIndex = 0;
static volatile bool sIsThreadComplete = false;
static int sArray[10000][100];

static void* NormalSumProc(void *param) {
    int currIndex;
    while((currIndex = sNormalIndex++) < 10000) {
        uint64_t sum = 0;
        for (int i = 0;i < 100; i++) {
            sum += sArray[currIndex][i];
        }
        sNormalResult += sum;
    }
    sIsThreadComplete = true;
    return NULL;
}

static void* AtomSumProc(void *param) {
    int currIndex;

    while((currIndex = atomic_fetch_add(&sAtomIndex,1))<10000) {
        uint64_t sum = 0;
        for (int i = 0; i < 100; ++i) {
            sum += sArray[currIndex][i];
        }
        atomic_fetch_add(&sAtomResult,sum);
    }
    sIsThreadComplete = true;
    return NULL;
}

int main(int argc,const char* argv[]){
    for (int i = 0; i < 10000; ++i) {
        for (int j = 0; j < 100; ++j) {
            sArray[i][j] = 100 * i + j;
        }
    }
    unsigned long long standardResult = 0;
    for (int i = 0; i < 10000; ++i) {
        for (int j = 0; j < 100; ++j) {
            standardResult += sArray[i][j];
        }
    }
    printf("The standard result is %llu\n",standardResult);

    pthread_t pthreadID;
    pthread_t threadID;
    pthread_create(&threadID,NULL,&NormalSumProc,NULL);

    int currIndex;
    while((currIndex = sNormalIndex++)<10000){
        uint64_t sum = 0;
        for (int i = 0; i < 100; ++i) {
            sum += sArray[currIndex][i];
        }
        sNormalResult += sum;
    }
    while(!sIsThreadComplete);
    if (sNormalResult == standardResult) {
        puts("Normal compute compared equal!");
    } else {
        printf("Normal compute compared not equal: %lu\n",sNormalResult);
    }
    sIsThreadComplete = false;
    pthread_create(&threadID,NULL,&AtomSumProc,NULL);
    while((currIndex = atomic_fetch_add(&sAtomIndex,1))<10000){
        uint64_t sum = 0;
        for (int i = 0; i < 100; ++i) {
            sum += sArray[currIndex][i];
        }
        atomic_fetch_add(&sAtomResult,sum);
    }
    while(!sIsThreadComplete);
    if (atomic_load(&sAtomResult) == standardResult) {
        puts("Atom compute compared equal!");
    } else {
        puts("Atom compute compared not equal!");
    }
}

代码设计不再多说。

The standard result is 499999500000
Normal compute compared not equal: 498547236350
Atom compute compared equal!

注意:修改CMakeLists.txt

cmake_minimum_required(VERSION 3.21)
project(CAtomic C)

set(CMAKE_C_STANDARD 11)

SET(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-pthread")

add_executable(CAtomic main.c)

参考

C语言编程魔法书

相关文章

  • C语言原子操作

    什么是原子操作 原子操作指的是对原子对象的读和写是不可被打断的。一个操作不可被打断意味着在执行整个操作过程中,即便...

  • C语言的原子操作

    原子操作,即不可分割开的操作;该操作一定是在同一个cpu时间片中完成,这样即使线程被切换,多个线程也不会看到同一块...

  • C语言的原子操作

    gcc支持如下原子操作:

  • C语言的原子操作

    C语言原子操作是在C11(C11:标准是C语言标准的第三版,前一个标准版本是[C99]标准)引入的,定义在头文件 ...

  • Java并发机制底层实现原理-原子操作的实现原理

    章节目录 原子操作含义 相关术语 保证多处理器操作原子性的两种方式 Java语言层面上实现原子操作 原子操作的含义...

  • Go语言——原子操作

    Go语言——原子操作 参考: 《Go并发编程实战(第2版)》 Background 原子操作即执行过程不能被中断的...

  • Go语言 原子操作

    原子操作就是不可中断的操作,外界是看不到原子操作的中间状态,要么看到原子操作已经完成,要么看到原子操作已经结束。在...

  • Unsafe - java堆对象的常用操作解析

    java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作,以及堪比C语言的...

  • 原子类型与原子操作

    原子类型 c++11提供了原子操作类型, 模板类std::atomic。头文件#include 内存模型 在原子类...

  • 2018-11-11 #C++ 内存模型#

    C++ 原子操作内存模型 C++ 原子操作内存模型解决并发编程的什么问题? 相信在大多数应用程序员眼里,代码在编辑...

网友评论

      本文标题:C语言原子操作

      本文链接:https://www.haomeiwen.com/subject/mfduhdtx.html