C11中_Atomic的“临界区”是什么

问题描述 投票:0回答:1

根据这个cppreference page,对于

_Atomic int a
++a
/
a++
/
a %= 2
/等都是原子的。但问题是,会不会出现下面的表达式:

_Atomic size_t thread_counter = 0;
void thread_counting() {
  int execution_id = (thread_counter++) % THREAD_NUM;
}

像使用

mutex
?:

实现一样“原子”
pthread_mutex_t my_mutex;
size_t thread_counter = 0;
void thread_counting() {
  pthread_mutex_lock(&my_mutex); // assume pthread_mutex_lock() always success
  int execution_id = (thread_counter++) % THREAD_NUM;
  pthread_mutex_unlock(&my_mutex);
}

C11标准的措辞对我来说有点太深奥了,我没能从中得到具体的答案。我准备了以下代码来测试这个假设,结果似乎表明答案是肯定的,但不确定其中是否有任何大缺陷。

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

#define THREAD_NUM 5000
#define PASS_NUM 128
_Atomic uint8_t *table = NULL;
_Atomic size_t thread_counter = 0;
_Atomic short error = 0;

void *counting(void *pass) {
  uint8_t p = *((uint8_t *)pass);
  if (error) {
    return NULL;
  }
  int execution_id = (thread_counter++) % THREAD_NUM;
  // Alternative implementation, breaks atomicity:
  // thread_counter = (thread_counter + 1) % THREAD_NUM;
  // int execution_id = thread_counter;
  if (table[execution_id] == p) {
    table[execution_id] = p + 1;
  } else {
    fprintf(stderr, "ERROR: table[%d]==%u, expecting %u\n", execution_id,
            table[execution_id], p);
    ++error;
  }
  return NULL;
}

void test_concurrent_counting() {
  table = (_Atomic uint8_t *)calloc(THREAD_NUM, sizeof(short));
  if (table == NULL) {
    perror("calloc()");
    goto err_calloc_table;
  }
  pthread_t *ths = (pthread_t *)calloc(THREAD_NUM, sizeof(pthread_t));
  if (table == NULL) {
    perror("malloc()");
    goto err_malloc_pthread;
  }

  for (uint8_t i = 0; i < PASS_NUM && error == 0; ++i) {
    printf("Pass no.%u\n", i);
    int j = 0;
    for (; j < THREAD_NUM; ++j) {
      if (pthread_create(&ths[j], NULL, counting, &i) != 0) {
        // this error handling might not be perfect
        --j;
        perror("pthread_create()");
        break;
      }
    }
    for (int k = 0; k <= j; ++k) {
      (void)pthread_join(ths[k], NULL);
    }
  }

  free(ths);
err_malloc_pthread:
  free(table);
err_calloc_table:
  return;
}

int main(void) { 
  test_concurrent_counting();
  return 0;
}
c atomic
1个回答
1
投票

你的代码是线程安全的,因为

THREAD_NUM
是常量。因为在

int execution_id = (thread_counter++) % THREAD_NUM;

execution_id
- 是一个局部变量,因此它不在线程之间共享。 并且对共享变量
thread_counter++
thread_counter
操作通过其
_Atomic
声明是原子的,因此并发线程看不到它部分执行。

所以它相当于

pthread_mutex_lock(&my_mutex); // assume pthread_mutex_lock() always success
int execution_id = (thread_counter++) % THREAD_NUM;
pthread_mutex_unlock(&my_mutex);
© www.soinside.com 2019 - 2024. All rights reserved.