C读写器线程

时间:2014-05-05 21:43:40

标签: c multithreading thread-safety pthreads

我正在编写具有以下结构的C多线程程序:

struct mystruct {
    int a;
    int b;
    int c;
    int d;
} Data;


void *thr_1();
void *thr_2();


int main(int argc, char *argv[]) {
    pthread_t t_1,
          t_2;

    if (pthread_create(&t_1, NULL, thr_1, NULL)
     || pthread_create(&t_2, NULL, thr_2, NULL)) {
        perror("pthread_create() on main()");
        return -1;
    }

    while (a < 2000) {
    /* do a lot of stuff with Data (never writes on it) */
    }

    pthread_cancel(thr_1);
    pthread_cancel(thr_2);
    pthread_join(thr_1, NULL);
    pthread_join(thr_2, NULL);

    return 0;
}

/* Threads */
void *thr_1() {
    while (1) {
        /* Read from stream and write to Data */
        usleep(50000);
    }
    return NULL;
}

void *thr_2() {
    while (1) {
        /* do some stuff with Data (never writes on it) */
        usleep(50000);
    }
    return NULL;
}

此代码正常运行,但经过一些研究(竞争条件和线程安全)之后,由于这些竞争条件,此代码随时都会失败。我的第一个想法是使用互斥锁在写入时锁定结构的成员并在之后释放它,但是对主循环和thr_2中的数据结构有太多的读访问权限。到目前为止,我的解决方案是创建2个访问函数,一个用于读取数据,一个用于写入数据,在这些函数内部,使用互斥锁来锁定写入。这个丑陋的解决方案似乎更像是对我的破解......所以,最后,我的问题是:有更好的方法来做到这一点吗?最好没有访问数据所需的任何功能。

非常感谢!

2 个答案:

答案 0 :(得分:1)

C11支持原子操作。不幸的是,大多数编译器不支持C11原子,但你可以使用GCC 4.9:

#include <stdatomic.h>

struct mystruct
{
    _Atomic int a;
    ...
};

int load(struct mystruct* s)
{
    return atomic_load(&s->a);
}

void store(struct mystruct* s, int value)
{
    atomic_store(&s->a, value);
}

请注意,仅保护写操作是不够的。 C11标准非常明确。如果两个线程可以同时访问同一个内存位置,并且至少有一个操作是写访问,那么程序的行为是不确定的。这意味着你必须保护所有访问。

答案 1 :(得分:0)

您真的需要更多信息才能做出真正明智的回复,但需要一些快速的信息。

还可以使用增强型[{3}}来提升原子,这将允许您进行以下操作。

struct mystruct 
{
    boost::atomic<int> a;
    boost::atomic<int> b;
    boost::atomic<int> c;
    boost::atomic<int> d;
}Data;

boost应该根据平台处理所有问题,因为有些人没有完全合格的CAS和/或内存障碍。

沿着这些方向你也可以使用boost中的boost::atomic<>结构之一。请记住,在这两种情况下,atmoics和lockfree,最好的情况是它们是用CAS操作实现的,最坏的情况可能更加丑陋。甚至CAS也不是免费的,因为操作可能会清空CPU指令缓存,导致执行延迟。

但是,如果您的处理可以容忍数据的延迟更新,则根据您的使用情况(如您所指出的只有一个线程写入数据),则不需要同步。