【问题标题】:Accessing variable from two threads in C从C中的两个线程访问变量
【发布时间】:2014-09-02 08:51:16
【问题描述】:

我有以下代码:

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

不断循环等待指针被另一个线程设置。另一个线程会简单地做

ptr = //some value

我的问题是,这安全吗?这是否会导致任何内存损坏,从而导致以后难以调试错误情况?

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

【问题讨论】:

  • 考虑使用原子操作,如本参考en.cppreference.com/w/c/atomic 中所述,它是在 C11 规范中引入的。 atomic_compare_exchange_strong是你循环中需要的。
  • 看起来很安全,赋值操作是原子的。但是,这种方法并不明智,如果在 sleep() 中,ptr 被更改为某个值并再次变回 NULL 怎么办?
  • @someuser:它并不总是安全的。取决于处理器(及其与其他内核的缓存一致性机制)和对齐方式(某些处理器可以加载未对齐的数据,但这种加载不是原子的)。
  • @BasileStarynkevitch 我同意比较,可能会出错,但这怎么会导致崩溃?我不明白缓存一致性和对齐的意义
  • @jogabonito 考虑编译器是否优化ptrNULL 的比较,方法是减少ptr,测试高位,然后增加ptr 以恢复它。如果对 ptr 的更改介于这些操作之间,ptr 将关闭 1 并且取消引用它可能会崩溃。

标签: c multithreading thread-safety


【解决方案1】:

这个问题无法按要求回答。 C 语言不支持线程。因此,如果您有线程,您将获得一些标准或库的支持,这些标准或库必须记录什么是安全的,什么不是。最有可能的是,线程标准说在一个线程上修改一个对象,而另一个线程正在或可能正在访问它是未定义的行为。在那种情况下,它肯定是不安全的。但是你需要检查你编码的特定标准。

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

【讨论】:

    【解决方案2】:

    至少,ptr 可以声明为 volatile ;但这是not 足够了。你真的想要atomic 操作。使用C11,您将拥有&lt;stdatomic.h&gt; 标准标题。 最近的 GCC 有你应该使用的 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 编译)。

    我还建议使用最新的工具(最新版本 4.9 的 GCC -in 2014 年 9 月-、最新的 binutils、最新的 gdb、最新的 libc、最新的内核......)

    【讨论】:

    • 没有太多的答案或解释(但它是一个很好的选择)。请详细说明。 OP 需要了解原因。
    • 我的问题是代码是否会导致任何未定义的行为。由于缺少 volatile 关键字,编译器可能优化 ptr 的事实对我来说并不重要。
    • 是的,你的代码会导致 UB,但在大多数 x86 机器上你可能不会注意到它......
    • IIRC Postgresql 有一个类似的错误,它只出现在非 x86 机器上。但我现在找不到任何参考资料!
    • 糟糕的答案。既然volatile既非必要也不充分,那么使用它有什么意义呢?
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-28
      • 2013-07-19
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      相关资源
      最近更新 更多