LLVM 编译器——这是一个优化错误吗?

LLVM compiler - Is this an optimization bug?

我有一个与 LLVM 编译器优化级别相关的有趣问题。我正在使用:

最好用示例代码来说明。我把问题归结为一个简单的 objective-c class。请先看下面的代码:

@interface Foo()  {
    BOOL is_loading;
}
@end

@implementation Foo

- (void)test {

    printf("started loading \n");

    // set loading flag to YES
    is_loading = YES;

    // schedule a timer to fire in 2 seconds, to simulate the end of loading
    [NSTimer scheduledTimerWithTimeInterval:2.0
                                     target:self
                                   selector:@selector(timerFired)
                                   userInfo:nil
                                    repeats:NO];

    // wait asynchronously until loading flag is set to NO
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        while (is_loading) {
            // loop until timer event modifies is_loading flag 
        }

        printf("finished loading \n");
    });
}

- (void)timerFired {

    printf("timer fired \n");

    // set loading flag to NO
    is_loading = NO;

}

@end

如果你实例化classFoo并调用load方法,它会模拟一个加载进度,并异步观察is_loading标志来判断是否加载完成。

之后,控制台输出将是这样的:

started loading
timer fired
finished loading  

但是如果你打开编译器优化,你会看到这个输出:

started loading
timer fired

显然 while 循环永远不会结束并且执行无法到达下一个 printf() 消息。

我是否遗漏了发生这种情况的明显原因,或者它可能是优化程序错误?

正如 Apple 在其 synchronization page 中所述,编译器在优化代码时可能不会多次加载该变量。它不知道它可能是从另一个线程编辑的,所以会发生这种情况。

将变量标记为 volatile 将强制编译器在每次需要时加载该值,而这不会发生。

即使是 Sami 的回答也是对您问题的回答,这可能会产生误导。

由于 volatile 变量 不是 线程安全的,当块形成两个不同的线程访问 is_loading 时,您的整个方法可能会失败。 volatile 的用法是 而不是 用于线程同步。

改用 GCD 信号量。