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

xdnvmnnf  于 2023-11-16  发布在  其他
关注(0)|答案(1)|浏览(87)

根据这个cppreference page,对于_Atomic int a++a/a++/a %= 2/etc都是原子的。但问题是,将下面的表达式:

_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;
}

ujv3wf0j

ujv3wf0j1#

如果THREAD_NUM是常量,那么您的代码是线程安全的。

int execution_id = (thread_counter++) % THREAD_NUM;

字符串
execution_id-是一个局部变量,所以它不会在线程之间共享。而对共享变量thread_counterthread_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);

相关问题