我有一个与LLVM编译器的优化级别相关的有趣问题。我正在使用:
最好用示例代码解释它。我将问题归结为一个简单的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()消息。
我是否错过了发生这种情况的明显原因,或者它是否是优化错误?
答案 0 :(得分:2)
当Apple在synchronization page上声明时,编译器在优化代码时可能不会多次加载变量。它不知道它可以从另一个线程编辑,所以这发生了。
将变量标记为volatile
将强制编译器在每次需要时加载值,这不会发生。
答案 1 :(得分:1)
即使萨米的回答也是对你问答的回答,也许会产生误导。
由于volatile
变量不线程安全,当块形成两个不同的线程访问is_loading
时,整个方法可能会失败。 volatile
的用法不用于线程同步。
改为使用GCD信号量。