2014-09-02 59 views
4

我已在下面的代码段:访问来自两个线程变量用C

int attempts = 0; 
while(ptr== NULL && attempts < 60) { 
     sleep(1000); 
     attempts++; 
    } 

连续环路等待指针由另一个线程来设置。另一个线程只会做

ptr = //some value 

我的问题是,这是安全的吗?这会导致任何内存损坏,导致以后很难调试错误条件?

P.S:我知道由于缺少volatile关键字,编译器可能会优化ptr。这对我来说并不重要。我只关心这是否会给代码的其他不相关部分带来问题。

+0

考虑使用此参考文献中描述的原子操作http://en.cppreference.com/w/c/atomic它是在C11规范中引入的。 'atomic_compare_exchange_strong'是你在循环中需要的。 – bkausbk 2014-09-02 09:04:16

+0

看起来很安全,赋值操作是原子的。但是,这种方法并不理智,如果在你睡觉的时候,ptr会被改变成某个值,然后又变回NULL。 – 2014-09-02 09:09:41

+0

另一个线程只会执行 - 只有一个写入线程?如果是的话,它似乎是安全的,因为它是简单的汇编指令(x86上的mov)。如果不是,那么这是不安全的。 – someuser 2014-09-02 09:19:01

回答

2

至少,ptr可以声明为volatile;但那就是not够了。你真的想要atomic操作。用C11你有<stdatomic.h>标准头。 最近的海湾合作委员会有atomic builtins你应该使用。您的ptr的访问和写入(在“您的其他线程”中)应该是原子的!

实际上(不使用原子操作),您会观察到的行为是undefined behavior,并且可能会有很大差异(使用不同的处理器,不同的编译器等)。

可悲的是,许多x86处理器,你可能没有注意到,UB

您需要编译器执行/通过发射特定的机器指令使用cache coherence

你也可以使用带互斥锁的condition variables,或者一些semaphore

最近的GCC(至少4.9),如果你的目标处理器支持,你可以考虑编译-fsanitize=thread和/或-fsanitize=address(用于调试)。

顺便说一句,你的内存损坏可能是完全无关的。你可以考虑使用在many platforms上支持的valgrind(最好用-g来编译你的程序,如果你愿意的话,你可以试着编译gcc -O1 -g)。

我还建议使用最新的工具(GCC最新的4.9版本-in月2014-,新版本binutils,最近GDB,最近的libc,最近的内核....)

+1

没有太多的答案或解释(但它是一个很好的选择)。请详细说明。 OP需要理解为什么。 – 2014-09-02 08:54:37

+0

我的问题是代码是否会导致任何未定义的行为。由于缺乏volatile关键字,编译器可能优化ptr的事实对我来说并不重要。 – rubndsouza 2014-09-02 08:57:56

+0

是的,你的代码会导致UB,但是在大多数x86机器上你可能不会注意到它...... – 2014-09-02 08:59:03

0

据我所知,没有。这是不安全的,可能会导致读取不一致。最好的方法是用互斥或二进制信号保护变量ptr。检查互斥函数的mutex.h(例如mutex_init,mutex_lock,mutex_unlock等)。

3

这个问题根本不可能回答。 C语言不支持线程。所以如果你有线程,你会从一些标准或库中获得支持,这些标准或库需要记录什么是安全的,哪些不是。最有可能的是,线程标准说修改一个线程上的一个对象,而另一个线程访问它可能是未定义的行为。在这种情况下,这绝对不安全。但是你需要检查你编码的特定标准。

顺便说一下,您无法通过测试来确定它是否安全。它可能看起来很安全,或者看起来像你在测试中所期望的那样,然后以不同的操作系统版本,不同的CPU,不同的编译器选项,不同的BIOS设置,甚至仅仅是由于运气不佳而失败。