2017-04-05 3 views
3

Ich habe ein interessantes Problem im Zusammenhang mit der Optimierungsstufe des LLVM-Compilers. Ich verwende:LLVM-Compiler - Ist das ein Optimierungsfehler?

  • Xcode 8.2.1
  • LLVM 8,0

Es ist besser, es mit einem Beispiel-Code zu erklären. Ich kochte das Problem in eine einfache Ziel-C-Klasse. Bitte beachten Sie den Code unten zuerst:

@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 

Wenn Sie Klasse instanziiert Foo und rufen load Methode, es wird ein Ladevorgang simulieren und asynchron beobachten is_loading Flag, um zu bestimmen, ob das Laden abgeschlossen ist.

Und danach wird die Konsole Ausgabe wie folgt sein:

started loading 
timer fired 
finished loading 

Aber wenn Sie auf der Compiler-Optimierung einschalten, werden Sie diese Ausgabe statt sehen:

started loading 
timer fired 

Anscheinend ist der while-Schleife endet nie und die Ausführung kann die nächste printf() Nachricht nicht erreichen.

Fehle mir ein offensichtlicher Grund dafür, oder kann es ein Optimierungsfehler sein?

Antwort

2

Wie Apple auf ihrem synchronization page angibt, kann der Compiler die Variable möglicherweise nicht mehr als einmal laden, wenn der Code optimiert ist. Es weiß nicht, dass es von einem anderen Thread bearbeitet werden kann, damit dies geschieht.

Wenn Sie die Variable als volatile markieren, wird der Compiler gezwungen, den Wert jedes Mal zu laden, wenn er benötigt wird. Dies wird nicht passieren.

+0

Danke, das hat funktioniert ... Es ist Zeit, mein Wissen über 'volatile' zu ​​kürzen :) – scener

+1

Ich möchte hinzufügen, dass das einfache Hinzufügen von' volatile' macht * den * Code nicht sicher, so dass es immer noch fehlschlagen kann. –

+2

Es ist bedauerlich, dass diese Antwort akzeptiert wurde. Busy-Looping auf einem volatilen kann in diesem Fall funktionieren, aber es wird einige regelrechte Nebenwirkungen haben, wie zum Beispiel die maximale Auslastung Ihrer CPU. –

1

Sogar Samis Antwort ist eine Antwort auf Ihr Q, es ist vielleicht irreführend.

Da volatile Variablen sind nicht Thread-sicher, Ihr können ganze Ansatz scheitern, wenn Blöcke zwei verschiedenen Threads zugreifen is_loading bilden. Die Verwendung von volatile ist nicht für Thread-Synchronisation vorgesehen.

Verwenden Sie stattdessen GCD-Semaphoren.