2016-08-07 14 views
2

Ich habe diese Methode bekamIOS-Thread-Pool

-(void)addObjectToProcess(NSObject*)object; 

und ich möchte diese Methode das Objekt in Prozess Warteschlange hinzuzufügen, die bis zu 4 Objekte gleichzeitig verarbeiten kann.

ich habe meine eigene dispatch_queue erstellt und semhphore

_concurrentQueue = dispatch_queue_create([queue_id UTF8String],DISPATCH_QUEUE_CONCURRENT); 
_processSema = dispatch_semaphore_create(4); 

und die Durchführung des Verfahrens ist:

-(void)addObjectToProcess(NSObject*)object { 
    dispatch_semaphore_wait(self.processSema, DISPATCH_TIME_FOREVER); 
    __weak MyViewController* weakSelf = self; 

    dispatch_async(self.concurrentQueue, ^{ 
     // PROCESS........... 
     // .................. 
     dispatch_semaphore_signal(self.processSema); 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      // call delegate from UI thread 
     }); 
    }); 
    } 

scheint es der Anrufer manchmal blockiert Ursache für die Semaphore Barriere bekommt.

Gibt es eine andere/einfachere Option zu implementieren, was ich hier zu machen versuche?

Dank

+1

Übrigens hat Ihr Code neben dem Semaphor-Problem zwei weitere Probleme. Erstens wird das 'weakSelf' nicht das erreichen, was Sie wollen, denn es gibt eine Referenz" self "innerhalb des Verschlusses, die einen starken Bezug zum View-Controller hat. Die schwache Referenz ist also nichts. Zweitens ist es nicht ausreichend, diese "Selbst" -Referenz innerhalb des Verschlusses durch "weakSelf" zu ersetzen, da dies eine Race-Bedingung einführt. Sie müssen eine neue "strongSelf" -Referenz innerhalb dieses Abschlusses erstellen (nicht auf "self" bezogen, sondern nur einen neuen starken Verweis auf "weakSelf" erstellen). – Rob

Antwort

3

Das Problem ist, dass Sie dispatch_semaphore_wait aus welchem ​​Thread sind rufen Sie addObjectToProcess auf (vermutlich der Hauptthread) genannt. Wenn Sie also bereits vier Tasks ausführen, wartet dieser fünfte Prozess auf den Hauptthread.

Sie möchten jedoch nicht nur das Warten auf den Semaphor in den Block verschieben, der an self.concurrentQueue gesendet wurde. Da dies die PROCESS-Tasks erfolgreich auf vier beschränken kann, verbrauchen Sie einen anderen Worker-Thread für jede dieser rückstandsfrei versendeten Aufgaben, und es gibt eine endliche Anzahl dieser Worker-Threads. Und wenn Sie diese ausschöpfen, können Sie andere Prozesse nachteilig beeinflussen.

Eine Möglichkeit, dies zu beheben, wäre das Erstellen einer seriellen Terminierungswarteschlange zusätzlich zu Ihrer Warteschlange für die gleichzeitige Verarbeitung, und dann diese gesamte Planungsaufgabe asynchron an diese Terminierungswarteschlange senden. So genießen Sie die maximale Parallelität in der Prozesswarteschlange, während Sie weder den Hauptthread blockieren noch Arbeitsthreads für gestaute Aufgaben verwenden. Zum Beispiel:

@property (nonatomic, strong) dispatch_queue_t schedulingQueue; 

Und

self.schedulingQueue = dispatch_queue_create("com.domain.scheduler", 0); 

Und

- (void)addObjectToProcess(NSObject*)object { 
    dispatch_async(self.schedulingQueue, ^{ 
     dispatch_semaphore_wait(self.processSema, DISPATCH_TIME_FOREVER); 
     typeof(self) __weak weakSelf = self; 

     dispatch_async(self.concurrentQueue, ^{ 
      // PROCESS........... 
      // .................. 
      typeof(self) __strong strongSelf = weakSelf; 
      if (strongSelf) { 
       dispatch_semaphore_signal(strongSelf.processSema); 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        // call delegate from UI thread 
       }); 
      } 
     }); 
    }); 
} 

Ein weiterer guter Ansatz (vor allem, wenn der "PROCESS" synchron ist) ist NSOperationQueue zu verwenden, die eine maxConcurrentOperationCount hat, die Kontrollen der Grad der Nebenläufigkeit für Sie. Zum Beispiel:

@property (nonatomic, strong) NSOperationQueue *processQueue; 

Und initialisieren:

self.processQueue = [[NSOperationQueue alloc] init]; 
self.processQueue.maxConcurrentOperationCount = 4; 

Und dann:

- (void)addObjectToProcess(NSObject*)object { 
    [self.processQueue addOperationWithBlock:^{ 
     // PROCESS........... 
     // .................. 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      // call delegate from UI thread 
     }); 
    }]; 
} 

Der einzige Trick ist, wenn der "Prozess", selbst asynchron ist. Wenn Sie das tun, können Sie nicht einfach addOperationWithBlock verwenden, sondern müssen Ihre eigene benutzerdefinierte, asynchrone Unterklasse NSOperation schreiben und dann addOperation an die NSOperationQueue. Es ist nicht schwer, asynchrone Unterklasse NSOperation zu schreiben, aber es sind ein paar kleine Details damit verbunden. Siehe Configuring Operations for Concurrent Execution in der Concurrency Programming Guide.