多个线程可以对数组中的不同字节进行“原子”无锁写入吗?

Posted

技术标签:

【中文标题】多个线程可以对数组中的不同字节进行“原子”无锁写入吗?【英文标题】:Can multiple threads do "atomic" lockfree writes to different bytes in an array? 【发布时间】:2021-09-10 09:20:43 【问题描述】:

N 线程可以在Mx-bit 元素的数组中执行长度为kx-bit 元素的非重叠间隔的“原子”无锁写入(最多N M)?

例如。假设N10M10k 是1,x8

然后我们有10线程,10字节数组,每个线程将1字节写入数组。 (根据假设,每个线程写入一个不同的字节,因为间隔是不重叠的。)


这是一个测试这是否可能的程序,或者一个线程是否“踩到他邻居的脚趾”。

/*
t gcc-8  bytes1.c -o bytes1  -lpthread  &&  t ./bytes1
*/
#include <stdint.h>
typedef  uint8_t   u8;
typedef  int32_t  i32;
typedef  int64_t  i64;

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

// ----------------------------------------------------------------------------------------------------------------------------#
/* @blk1  test if multiple threads can do 1-byte aligned lockfree atomic writes to an array! */
#define THRS_TEST_LOCKFREE_WRITES_DX    0x20
#define THRS_TEST_LOCKFREE_WRITES_TMUL  2  // thread multiplier (for the numbre of threads)!

typedef struct
  i32       idx;
  i32       tdim;
  pthread_t pthr;

  i32       dx;
  u8*       g_data;
thr_t;

void* thr_test_lockfree_write(void* arg)
  thr_t* thr = (thr_t*)arg;
  for(int i=0; i<thr->dx; ++i)
    thr->g_data[thr->idx*thr->dx + i] = thr->idx;
  return NULL;


void thrs_test_lockfree_writes()
  i32    thrs_idim = THRS_TEST_LOCKFREE_WRITES_TMUL * sysconf(_SC_NPROCESSORS_ONLN);
  thr_t* thrs      = alloca(sizeof(thr_t)*thrs_idim);
  i32    x_dx      = THRS_TEST_LOCKFREE_WRITES_DX;
  i64    x_idim    = x_dx*thrs_idim;
  u8*    x_data    = aligned_alloc(0x1000, sizeof(u8)*x_idim);

  for(int i=0; i<thrs_idim; ++i)
    thrs[i].idx    = i;
    thrs[i].tdim   = thrs_idim;
    thrs[i].dx     = x_dx;
    thrs[i].g_data = x_data;
    pthread_create(&thrs[i].pthr, NULL, thr_test_lockfree_write, &thrs[i]);
  

  for(int i=0; i<thrs_idim; ++i)
    pthread_join(thrs[i].pthr, NULL);
  

  putchar(0x0a);
  for(int i=0; i<x_idim/x_dx; ++i)
    printf("\x1b[32m%02x\x1b[91m/\x1b[37m%02x  \x1b[0m", i,thrs_idim-1);
    i32 flag = 1;
    for(int j=0; j<x_dx; ++j)
      printf("%02x", x_data[i*x_dx+j]);
      flag &= x_data[i*x_dx+j]==i%0x100;
    
    printf(" %d\n",flag);
  

  free(x_data);


// ----------------------------------------------------------------------------------------------------------------------------#
int main()
  thrs_test_lockfree_writes();
  puts("\nbye!");

【问题讨论】:

【参考方案1】:

N 个线程能否执行“原子”无锁写入

在可以将单个字节写入内存的架构上(这是所有现代架构),是的。

另请参阅this answer,并注意,由于false sharing,您的程序实际实现的并行度将远低于预期。

【讨论】:

这很有趣。根据article,多线程代码 with 错误共享有时可能比串行代码没有错误共享(尽管我希望这是主要适用于罕见的退化情况,例如一遍又一遍地更新同一个字节)。 @étale-cohomology,许多特定于并行代码的性能损失在某些情况下会产生如此大的影响,以至于相同任务的串行代码可以与之相比。

以上是关于多个线程可以对数组中的不同字节进行“原子”无锁写入吗?的主要内容,如果未能解决你的问题,请参考以下文章

是否存在多个读取或写入线程的无锁队列?

无锁机制下的原子性操作

linux多线程--双buffer“无锁”设计

使用Interlocked在多线程下进行原子操作,无锁无阻塞的实现线程运行状态判断

无锁的原子函数改变两个独立的内存位置

Android-CAS与原子变量(无锁并发和有锁并发)