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.
Ü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