首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带栅栏的SPSC螺纹安全

带栅栏的SPSC螺纹安全
EN

Stack Overflow用户
提问于 2017-02-04 05:16:55
回答 1查看 126关注 0票数 0

我只想我的代码尽可能简单,线程安全。

与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标准,对吗?

代码语言:javascript
复制
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;
}

我将这些功能称为以下功能:

主线程对一些数据进行排队:

代码语言:javascript
复制
while (thrd_tryenq(thrd_get_queue(&tinfo[tnum]), i)) {
    usleep(10);
    continue;
}

其他线程排队列数据:

代码语言:javascript
复制
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代码,只需将栅栏替换为

代码语言:javascript
复制
asm volatile ("sfence" ::: "memory");

代码语言:javascript
复制
asm volatile ("lfence" ::: "memory");

(我对这些宏的理解是:编译器围栏以防止内存访问被重新化/优化+硬件栅栏)

例如,是否需要将我的变量声明为易失性?

我已经看到了上面只有这些asm栅栏,但没有原子类型的环形缓冲代码,我真的很惊讶,我想知道这段代码是否正确。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-02-04 07:57:24

关于C11原子,我只是回答,平台细节太复杂了,应该逐步淘汰。

C11中的线程之间的同步只能通过一些系统调用(例如mtx_t)和atomics来保证。甚至不要试着在没有。

也就是说,同步化是通过原子实现的,也就是说,副作用的可见性保证通过原子效应的可见性来传播。例如,对于最简单的一致性模型,每次线程T2看到修改线程T1对原子变量A产生影响时,线程T1中的调制之前的所有副作用对T2都是可见的。

因此,并不是所有共享变量都需要是原子的,您只需确保状态通过原子正确传播。从这个意义上说,当你使用顺序或获得释放一致性时,篱笆不会为你买到任何东西,它们只会使画面变得复杂。

一些更一般性的评论:

  • 由于您似乎使用了顺序一致性模型(这是默认的),所以原子操作的函数写入(例如atomic_load)是多余的。仅仅计算原子变量是完全相同的。
  • 我的印象是,在开发过程中,您尝试优化的时间太早了。我认为您应该先做一个可以证明正确性的实现。然后,当且仅当您注意到性能问题时,您应该开始考虑优化。这样的原子数据结构不太可能成为应用程序的真正瓶颈。你必须有大量的线程,这些线程都同时敲击你可怜的原子变量,才能在这里看到一个可测量的瓶颈。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42036754

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档