2017-04-05 52 views
3

我有一个有趣的问题与LLVM编译器的优化级别有关。我使用的是:LLVM编译器 - 这是一个优化错误吗?

  • 的Xcode 8.2.1
  • LLVM 8.0

最好是用一个例子代码来解释它。我把这个问题归结为一个简单的Objective-C类。请看下面的代码第一:

@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 

如果实例Foo类并调用load方法,它将模拟加载进度和异步观察is_loading标志来确定是否加载完毕。

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

started loading 
timer fired 
finished loading 

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

started loading 
timer fired 

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

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

回答

2

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

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

+0

谢谢,这工作..是时候把我的知识关于'volatile' :) – scener

+1

我想补充说,简单地添加'volatile'不会使代码线程安全,所以它仍然可能会失败。 –

+2

这个答案被接受是不幸的。在这种情况下,在volatile中繁忙循环可能会起作用,但它会产生一些令人讨厌的副作用,例如最大化CPU利用率。 –

1

即使萨米的答案是答案Q,它可能是误导。

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

改为使用GCD信号量。