2017-04-05 70 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信號量。