2016-04-14 10 views
2

der folgende Code-Snippet Gegeben:dispatch_barrier_sync Deadlocks immer

#import <XCTest/XCTest.h> 

@interface DispatchTests : XCTestCase { 
    dispatch_queue_t _workQueue; 
    dispatch_queue_t _readWriteQueue; 
    int _value; 
} 
-(void)read; 
-(void)write; 
@end 

@implementation DispatchTests 

-(void)testDispatch { 
    _workQueue = dispatch_queue_create("com.work", DISPATCH_QUEUE_CONCURRENT); 
    _readWriteQueue = dispatch_queue_create("com.readwrite", DISPATCH_QUEUE_CONCURRENT); 
    _value = 0; 
    for(int i = 0; i < 100; i++) { 
     dispatch_async(_workQueue, ^{ 
      if(arc4random() % 4 == 0) { 
       [self write]; 
      } else { 
       [self read]; 
      } 
     }); 
    } 
    XCTestExpectation* expectation = [self expectationWithDescription:@"dude"]; 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     [expectation fulfill]; 
    }); 

    [self waitForExpectationsWithTimeout:6.0 handler:nil]; 
} 

-(void)read { 
    dispatch_sync(_readWriteQueue, ^{ 
     NSLog(@"read:%d", _value); 
    }); 
} 

-(void)write { 
    dispatch_barrier_sync(_readWriteQueue, ^{ 
     _value++; 
     NSLog(@"write:%d", _value); 
    }); 
} 

@end 

Der Zweck dieses Tests ist zu sehen, ob ich ein dispatch_barrier verwenden, um eine Lese-/Schreibsperre zu verwalten. In diesem Test sind sowohl der Leser als auch der Schreiber synchron. Der Test scheint gut zu funktionieren, wenn ich die Barriere asynchron mache, aber ich möchte asynchrones Verhalten vermeiden, weil diese Implementierung nicht-trivial ist. Ich versuche zu verstehen, warum die write Methode Deadlocking ist. Entsprechend der GCD-Dokumentation:

Wenn der Barriereblock die Front einer privaten gleichzeitigen -Warteschlange erreicht, wird er nicht sofort ausgeführt. Stattdessen wartet die Warteschlange, bis ihre aktuell ausgeführten Blöcke die Ausführung beenden. An diesem Punkt führt die Warteschlange den Sperrblock selbst aus. Alle Blöcke, die nach dem Barriereblock übergeben werden, werden erst ausgeführt, wenn der Barriereblock abgeschlossen ist.

Ich bin verwirrt, was mit "derzeit ausgeführten Blöcken" gemeint ist.

Meine Interpretation ist dieses Szenario, in dem ein Haufen liest (x) vorgelegt bekommen, dann eine Schreib (y), dann mehr liest (z):

  • (x) führt
  • (y) bis wartet (x)
  • (y) Blöcke (z) von der Ausführung
  • (x) vervollständigt
  • (y) ausführt
  • (y) durchgeführt werden, vervollständigt
  • (z) führt
  • (z)
+0

Ich habe das Sample nicht getestet, aber nachdem ich es betrachtet habe, sollte es _not_ Deadlock nicht sein, IMHO. – CouchDeveloper

Antwort

3

OK abgeschlossen ist, nach der eigentlichen Prüfung es: Ihr Code nicht blockieren - in der Theorie.

Jedoch - in der Praxis - es kann.

Was Sie erleben, ist eine Situation, in der alle verfügbaren Systemthreads erschöpft sind. Um fortzufahren, würde Ihr Code erfordern, dass GCD einen neuen Thread erhält - aber nicht mehr verfügbar ist - und somit Deadlocks.

Um solche Situation zu vermeiden, müssen Sie Ihren Code analysieren, wo er ungebunden neue Threads erzeugt. Dies kann bei gleichzeitigen Warteschlangen auftreten, bei denen der Block blockiert oder zu lange dauert, bis er beendet ist und eine hohe Anzahl von Blöcken mit hoher Häufigkeit an diese gleichzeitige Warteschlange gesendet wird.

Zum Beispiel, wenn Sie eine kleine Verzögerung einfügen:

for(int i = 0; i < 400; i++) { 
    usleep(1000); 
    dispatch_async(_workQueue, ^{ 
     if(arc4random() % 4 == 0) { 
      [self write]; 
     } else { 
      [self read]; 
     } 

    }); 
} 

der Code ausgeführt, bis es regelmäßig beendet. Dies dient nur dazu, das Problem zu demonstrieren - nicht um Ihr Problem zu beheben.

+0

Dies scheint der Fall zu sein.Wenn ich den Schleifenzähler ohne Verzögerung auf 50 reduziere, läuft es gut. Leider wird die unbegrenzte Situation dadurch verursacht, dass 'MapKit' Kacheln für den' OverlayRenderer' anfordert und ich bin nicht sicher, wie ich es drosseln soll. –

+1

@TimReddy Die Lösung wäre, die maximale Anzahl gleichzeitiger Blöcke zu begrenzen, die in der gleichzeitigen Warteschlange ausgeführt werden. Das klingt wie, Sie müssen 'NSOperationQueue' verwenden, aber es gibt auch Ansätze, die eine Dispatch-Warteschlange und ein Semaphor verwenden. – CouchDeveloper

+2

Danke für den Tipp! Ich verwende jetzt einen 'dispatch_semaphore', um die Anzahl der Threads zu drosseln. –

Verwandte Themen