我只想我的代码尽可能简单,线程安全。
与C11原子
关于ISO/IEC 9899/201X草案第7.17.4部分
X和Y都是在某些原子物体M上操作的,例如在X,X修改M,Y在B之前对A进行排序,Y读取X写入的值或假设释放序列X中任何副作用所写的值,如果它是释放操作的话。
这个代码线程安全吗(以"w_i“作为”对象M")?
"w_i“和"r_i”都需要声明为_Atomic吗?
如果只有w_i是_Atomic,那么主线程是否可以在缓存中保留r_i的旧值,并认为队列不是满的(而它是满的)并写入数据呢?
如果我读到一个没有atomic_load的原子,会发生什么?
我做了一些测试,但我的所有尝试似乎都给出了正确的结果。但是,我知道我的测试对于多线程并不是真正正确的:我多次运行我的程序并查看结果。
即使没有w_i而不是r_i被声明为_Atomic,我的程序工作,但只有栅栏是不够的C11标准,对吗?
typedef int rbuff_data_t;
struct rbuf {
rbuff_data_t * buf;
unsigned int bufmask;
_Atomic unsigned int w_i;
_Atomic unsigned int r_i;
};
typedef struct rbuf rbuf_t;
static inline int
thrd_tryenq(struct rbuf * queue, rbuff_data_t val) {
size_t next_w_i;
next_w_i = (queue->w_i + 1) & queue->bufmask;
/* if ring full */
if (atomic_load(&queue->r_i) == next_w_i) {
return 1;
}
queue->buf[queue->w_i] = val;
atomic_thread_fence(memory_order_release);
atomic_store(&queue->w_i, next_w_i);
return 0;
}
static inline int
thrd_trydeq(struct rbuf * queue, rbuff_data_t * val) {
size_t next_r_i;
/*if ring empty*/
if (queue->r_i == atomic_load(&queue->w_i)) {
return 1;
}
next_r_i = (queue->r_i + 1) & queue->bufmask;
atomic_thread_fence(memory_order_acquire);
*val = queue->buf[queue->r_i];
atomic_store(&queue->r_i, next_r_i);
return 0;
}我将这些功能称为以下功能:
主线程对一些数据进行排队:
while (thrd_tryenq(thrd_get_queue(&tinfo[tnum]), i)) {
usleep(10);
continue;
}其他线程排队列数据:
static void *
thrd_work(void *arg) {
struct thrd_info *tinfo = arg;
int elt;
atomic_init(&tinfo->alive, true);
/* busy waiting when queue empty */
while (atomic_load(&tinfo->alive)) {
if (thrd_trydeq(&tinfo->queue, &elt)) {
sched_yield();
continue;
}
printf("Thread %zu deq %d\n",
tinfo->thrd_num, elt);
}
pthread_exit(NULL);
}带asm栅栏的
对于特定的平台x86,如果我删除所有C11代码,只需将栅栏替换为
asm volatile ("sfence" ::: "memory");和
asm volatile ("lfence" ::: "memory");(我对这些宏的理解是:编译器围栏以防止内存访问被重新化/优化+硬件栅栏)
例如,是否需要将我的变量声明为易失性?
我已经看到了上面只有这些asm栅栏,但没有原子类型的环形缓冲代码,我真的很惊讶,我想知道这段代码是否正确。
发布于 2017-02-04 07:57:24
关于C11原子,我只是回答,平台细节太复杂了,应该逐步淘汰。
C11中的线程之间的同步只能通过一些系统调用(例如mtx_t)和atomics来保证。甚至不要试着在没有。
也就是说,同步化是通过原子实现的,也就是说,副作用的可见性保证通过原子效应的可见性来传播。例如,对于最简单的一致性模型,每次线程T2看到修改线程T1对原子变量A产生影响时,线程T1中的调制之前的所有副作用对T2都是可见的。
因此,并不是所有共享变量都需要是原子的,您只需确保状态通过原子正确传播。从这个意义上说,当你使用顺序或获得释放一致性时,篱笆不会为你买到任何东西,它们只会使画面变得复杂。
一些更一般性的评论:
atomic_load)是多余的。仅仅计算原子变量是完全相同的。https://stackoverflow.com/questions/42036754
复制相似问题