Detaillierte Erläuterung der Versandwarteschlangen-API

Versandwarteschlange

„Alles, was der Entwickler tun muss, ist, die Aufgaben zu definieren, die er ausführen möchte, und sie der entsprechenden Dispatch-Warteschlange hinzuzufügen“, ausgedrückt im Code wie folgt:

    dispatch_async(queue, ^{
       /*
        * 想要执行的任务Code
        */
    });

Die Dispatch-Warteschlange im obigen Code stellt die Warteschlange für die Ausführung und Verarbeitung dar. Entwickler können die Warteschlange, die sie ausführen möchten, über den folgenden Code erstellen.

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

Klassifizierung der Versandwarteschlange

  1. Serielle Versandwarteschlange : Auch als private Versandwarteschlange bekannt , es wird jeweils nur eine Aufgabe gleichzeitig ausgeführt. Eine serielle Warteschlange belegt nur einen Thread und wird häufig verwendet, um den Zugriff auf bestimmte Ressourcen oder Daten zu synchronisieren. Wenn Sie mehrere serielle Warteschlangen erstellen, werden die seriellen Warteschlangen, obwohl jede synchronisiert ist, gleichzeitig ausgeführt.
  2. Gleichzeitige Versandwarteschlange : Auch als globale Versandwarteschlange bezeichnet. Mehrere Aufgaben können gleichzeitig ausgeführt werden, die Ausführungsreihenfolge ist jedoch zufällig. Das System stellt vier globale gleichzeitige Warteschlangen bereit. Diese vier Warteschlangen haben entsprechende Prioritäten. Benutzer können keine globalen Warteschlangen erstellen, sondern nur abrufen.
  3. Haupt-Dispatch-Warteschlange : Eine global verfügbare serielle Warteschlange, die Aufgaben im Haupt-Thread der Anwendung ausführt.

Dispatch_queue_creat

Erstellen Sie mit der Funktion „dispatch_queue_creat“ eine ausführbare Dispatch-Warteschlange.
Diese Funktion verfügt über zwei Parameter: den ersten benutzerdefinierten Warteschlangennamen, der zweite Parameter ist der Warteschlangentyp, der Standardwert NULL oder DISPATCH_QUEUE_SERIAL ist seriell und der Parameter DISPATCH_QUEUE_CONCURRENT ist eine parallele Warteschlange.

dispatch_queue_t queueCon, queueSerial;
queueCon = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
queueSerial = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);

Warteschlangenpriorität

Die Priorität der Warteschlange kann mit der Methode „dispatch_queue_attr_make_with_qos_class“ oder „dispatch_set_target_queue“ festgelegt werden.

dipatch_queue_attr_make_with_qos_class

 * Returns an attribute value which may be provided to dispatch_queue_create()
 * or dispatch_queue_create_with_target(), in order to assign a QOS class and
 * relative priority to the queue.
 *
 * @discussion
 * When specified in this manner, the QOS class and relative priority take
 * precedence over those inherited from the dispatch queue's target queue (if
 * any) as long that does not result in a lower QOS class and relative priority.
 *
 * The global queue priorities map to the following QOS classes:
 *  - DISPATCH_QUEUE_PRIORITY_HIGH:         QOS_CLASS_USER_INITIATED
 *  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      QOS_CLASS_DEFAULT
 *  - DISPATCH_QUEUE_PRIORITY_LOW:          QOS_CLASS_UTILITY
 *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:   QOS_CLASS_BACKGROUND
 *
 * Example:
 * <code>
 *  dispatch_queue_t queue;
 *  dispatch_queue_attr_t attr;
 *  attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
 *          QOS_CLASS_UTILITY, 0);
 *  queue = dispatch_queue_create("com.example.myqueue", attr);
 * </code>
  * @param attr
 * A queue attribute value to be combined with the QOS class, or NULL.
 *
 * @param qos_class
 * A QOS class value:
 *  - QOS_CLASS_USER_INTERACTIVE
 *  - QOS_CLASS_USER_INITIATED
 *  - QOS_CLASS_DEFAULT
 *  - QOS_CLASS_UTILITY
 *  - QOS_CLASS_BACKGROUND
 * Passing any other value results in NULL being returned.
 *
 * @param relative_priority
 * A relative priority within the QOS class. This value is a negative
 * offset from the maximum supported scheduler priority for the given class.
 * Passing a value greater than zero or less than QOS_MIN_RELATIVE_PRIORITY
 * results in NULL being returned.
 *
 * @return
 * Returns an attribute value which may be provided to dispatch_queue_create()
 * and dispatch_queue_create_with_target(), or NULL if an invalid QOS class was
 * requested.
 * The new value combines the attributes specified by the 'attr' parameter and
 * the new QOS class and relative priority.
 */

dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.qosqueue", attr);

„dispatch_set_target_queue“.

Sie können die Priorität festlegen und auch die Warteschlangenhierarchie festlegen, um beispielsweise die serielle Ausführung mehrerer serieller und paralleler Warteschlangen in derselben seriellen Warteschlange zuzulassen.

//dispatch_set_target_queue
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.settargetqueue",NULL); //需要设置优先级的queue
dispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
//设置queue和referQueue的优先级一样
dispatch_set_target_queue(queue, referQueue); 

//让多个串行和并行队列在统一一个串行队列里串行执行
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t firstQueue = dispatch_queue_create("com.starming.gcddemo.firstqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t secondQueue = dispatch_queue_create("com.starming.gcddemo.secondqueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_set_target_queue(firstQueue, serialQueue);
dispatch_set_target_queue(secondQueue, serialQueue);

dispatch_async(firstQueue, ^{
    NSLog(@"1");
    [NSThread sleepForTimeInterval:3.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"2");
    [NSThread sleepForTimeInterval:2.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"3");
    [NSThread sleepForTimeInterval:1.f];
});

Hauptversandwarteschlange/Globale Versandwarteschlange

Zusätzlich zum Abrufen der Warteschlange über die obige Erstellungsfunktion stellt GCD auch Standardwarteschlangen für die folgenden Systeme bereit.

Hauptversandwarteschlange

Die auf der Haupt-Thread-Uhr ausgeführte Warteschlange wird nur auf dem Haupt-Thread ausgeführt und gehört zur seriellen Warteschlange. Es wird hauptsächlich für einige Verarbeitungen verwendet, die im Hauptthread ausgeführt werden müssen, z. B. für die Aktualisierung der Schnittstelle.

queue = dispatch_get_main_queue();

Globale Versandwarteschlange

Gleichzeitige Warteschlange, die von allen von GCD bereitgestellten Aufgaben verwendet werden kann. Es ist nicht erforderlich, sie einzeln über die Funktion „dispatch queue_creat“ hinzuzufügen. Die globale Dispatch-Warteschlange hat die folgenden vier Ausführungsprioritäten:
– Hohe Priorität
– Standardpriorität
– Hintergrundpriorität
– Niedrige Priorität

Erstellen Sie eine Hauptwarteschlange und eine globale Warteschlange mit vier verschiedenen Prioritäten:

dispatch_get_main_queue();// Main Queue
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

„dispatch_queue“ verfügt über die folgenden fünf Warteschlangen:

  1. QOS_CLASS_USER_INTERACTIVE: Die Benutzerinteraktivitätsstufe gibt an, dass die Aufgabe sofort ausgeführt werden muss, um ein gutes Erlebnis zu bieten, und wird zum Aktualisieren der Benutzeroberfläche, zum Reagieren auf Ereignisse usw. verwendet. Diese Klasse sollte am besten klein gehalten werden.
  2. QOS_CLASS_USER_INITIATED: Die vom Benutzer initiierte Ebene gibt an, dass die Aufgabe von der Benutzeroberfläche asynchron ausgeführt wird. Das anwendbare Szenario liegt dann vor, wenn bei fortgesetzter Interaktion zeitnahe Ergebnisse erforderlich sind.
  3. QOS_CLASS_UTILITY: Die Dienstprogrammklasse zeigt lang laufende Aufgaben mit für den Benutzer sichtbaren Fortschrittsindikatoren an. Es wird häufig für Berechnungen, E/A, Netzwerk, kontinuierliche Datenfüllung und andere Aufgaben verwendet. Diese Aufgabe spart Energie.
  4. QOS_CLASS_BACKGROUND: Die Hintergrundebene weist auf Aufgaben hin, die der Benutzer nicht bemerkt, die zum Vorladen verwendet werden, oder auf Aufgaben, die keine Benutzerinteraktion erfordern und nicht zeitkritisch sind.
  5. HAUPTWARTESCHLANGE: Die Hauptwarteschlange, die zum Aktualisieren der Benutzeroberfläche verwendet wird

    • Hauptwarteschlange : „dispatch_after“ wird in diesem Typ verwendet, wenn sich Aufgaben in der Warteschlange befinden, die die Benutzeroberfläche aktualisieren müssen.
    • Gleichzeitige Warteschlange : Wird zum Ausführen von Hintergrundaufgaben verwendet, die nichts mit der Benutzeroberfläche zu tun haben. Dispatch_sync wird hier platziert, sodass bequem auf den Abschluss der Aufgabe gewartet werden kann, um sie später zu verarbeiten oder mit der Dispatch-Barriere zu synchronisieren. Es ist auch sinnvoll, hier Versandgruppen anzugeben.
    • Benutzerdefinierte sequentielle Warteschlange : Beim sequentiellen Ausführen und Verfolgen von Hintergrundaufgaben. Dadurch werden Ressourcenkonflikte vermieden, da jeweils nur eine Aufgabe ausgeführt wird. Hier werden die Dipatch-Barrieren zur Lösung des Lese-Schreibsperren-Problems behandelt. Hier werden auch die Versandgruppen platziert.
//可以使用下面的方法简化QoS等级参数的写法:

var GlobalMainQueue: dispatch_queue_t {
     return dispatch_get_main_queue()
}
var GlobalUserInteractiveQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.value), 0)
}
var GlobalUserInitiatedQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)
}
var GlobalUtilityQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)
}
var GlobalBackgroundQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)
}

//使用起来就是这样,易读而且容易看出在使用哪个队列
dispatch_async(GlobalUserInitiatedQueue) {
     let overlayImage = self.faceOverlayImageFromImage(self.image)
     dispatch_async(GlobalMainQueue) {
          self.fadeInNewImage(overlayImage)
     }
}

„dispatch_once“.

Wenn „dispatch_once_t“ eine globale oder statische Variable ist, stellen Sie sicher, dass es nur eine Instanz von „dispatch_once_t“ gibt. Das heißt, der Code im Dispatch_once-Block wird garantiert nur einmal ausgeführt. Wird normalerweise zur Instanziierung von Singletons verwendet.

+(instancetype)sharedNKPlayerTool{
  static dispatch_once_t once;
  dispatch_once(&once, ^{
      tool = [[NKPlayerTool alloc]init];
  });
  return tool;
}

„dispatch_set_target_queue“.

Unabhängig davon, ob die von „dispatch_creat_queue“ generierte Dispatch-Warteschlange eine serielle Warteschlange oder eine gleichzeitige Warteschlange ist, sind Threads mit derselben Ausführungspriorität wie die globale Dispatch-Warteschlange mit Standardpriorität anwendbar. Verwenden Sie „dispatch_set_target_queue“, um die Ausführungspriorität zu ändern.
Geben Sie die Dispatch-Warteschlange, deren Priorität geändert werden soll, als Parameter der Funktion „dispatch_set_target_queue“ an, die nicht nur die Ausführungspriorität der Dispatch-Warteschlange ändern kann, sondern auch als Ausführungsebene der Dispatch-Warteschlange dienen kann. Wenn Sie die Funktion „dispatch_set_target_queue“ verwenden, um ein Ziel als serielle Dispatch-Warteschlange in mehreren seriellen Dispatch-Warteschlangen anzugeben, gibt es mehrere serielle Dispatch-Warteschlangen, die parallel ausgeführt werden sollten, und es kann nur ein Prozess gleichzeitig in der seriellen Ziel-Dispatch-Warteschlange ausgeführt werden.

//dipatch_queue_attr_make_with_qos_class
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);
dispatch_queue_t queue = dispatch_queue_create("com.gcddemo.qosqueue", attr);

//dispatch_set_target_queue
dispatch_queue_t queue = dispatch_queue_create("com.gcddemo.settargetqueue",NULL); //需要设置优先级的queue
dispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
dispatch_set_target_queue(queue, referQueue); //设置queue和referQueue的优先级一样

Wenn nicht ausführbare Verarbeitung zu mehreren Dispatch-Warteschlangen hinzugefügt werden muss und das Ziel der Funktion „dispatch_set_target_queue“ als serielle Dispatch-Warteschlange angegeben ist, kann die parallele Verarbeitung verhindert werden.
Sie können die Priorität und auch die Warteschlangenhierarchie festlegen, um beispielsweise die serielle Ausführung mehrerer serieller und paralleler Warteschlangen in derselben seriellen Warteschlange wie folgt zu ermöglichen

dispatch_queue_t serialQueue = dispatch_queue_create("com.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t firstQueue = dispatch_queue_create("com.gcddemo.firstqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t secondQueue = dispatch_queue_create("com.gcddemo.secondqueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_set_target_queue(firstQueue, serialQueue);
dispatch_set_target_queue(secondQueue, serialQueue);

dispatch_async(firstQueue, ^{
    NSLog(@"1");
    [NSThread sleepForTimeInterval:3.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"2");
    [NSThread sleepForTimeInterval:2.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"3");
    [NSThread sleepForTimeInterval:1.f];
});

„dispatch_after“.

Die Ausführung von „dispatch_after“ fügt der Dispatch-Warteschlange zu einem bestimmten Zeitpunkt Verarbeitung hinzu, um die Ausführung des Codes zu verzögern.
„dispatch_after“ verzögert nur die Übermittlung des Blocks, nicht die sofortige Ausführung.

double delayInSeconds = 2.0;
     dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
     dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
          [self bar];
     });

Für die Versandzeitparameter im Beispiel können Sie sich zunächst den Funktionsprototyp ansehen

dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );

Der erste Parameter ist DISPATCH_TIME_NOW, was „aktuell“ bedeutet. Das Delta des zweiten Parameters stellt Nanosekunden dar, und eine Sekunde entspricht 1000000000 Nanosekunden. Das System stellt zur Vereinfachung einige Makros bereit

#define NSEC_PER_SEC 1000000000ull //每秒有多少纳秒
#define USEC_PER_SEC 1000000ull    //每秒有多少毫秒
#define NSEC_PER_USEC 1000ull      //每毫秒有多少纳秒

Wenn Sie also eine Sekunde ausdrücken möchten, können Sie sie so schreiben

dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);

„dispatch_after“ führt eine verzögerte Übermittlung von Werten vom Typ „dispatch_time_t“ durch, die mithilfe der Funktionen „dispatch_time“ oder „dispatch_walltime“ generiert werden.

// 得到从现在开始10秒后的dispatch_time_t,
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 10ull*NSEC_PER_SEC)

ull ist ein numerisches Literal in der C-Sprache und eine Zeichenfolge, die zur Anzeige des Typs verwendet wird.
Gibt an (unsighed long long)
– „dispatch_time“ wird normalerweise zur Berechnung der relativen Zeit verwendet,
– „dispatch_walltime“ wird zur Berechnung der absoluten Zeit verwendet

Der folgende Code wird verwendet, um das Datum im Fall der absoluten Zeit anzugeben:

NSTimeInterval interval;
 double second, subsecond;
 struct timespec time;
 dispatch_time_t walltime;
 interval = [date timeIntervalSince1970];
 time.tv_sec = second;
 time.tv_nsec = subsecond * NSEC_PER_SEC;
 walltime = dispatch_walltime(&time, 0)

„dispatch_barrier_async“.

Die Dispatch-Barriere stellt sicher, dass der übermittelte Abschluss der einzige in der angegebenen Warteschlange ist, der zu einem bestimmten Zeitpunkt ausgeführt wird. Dieser Abschluss wird erst dann ausgeführt, wenn alle Aufgaben vor der Dispatch-Barriere abgeschlossen sind. Wenn die Schließung an der Reihe ist, führt die Barriere die Schließung aus und stellt sicher, dass die Warteschlange während des Prozesses keine anderen Aufgaben ausführt. Die Warteschlange wird nach Abschluss der Schließung fortgesetzt. Es ist zu beachten, dass „dispatch_barrier_async“ diesen Effekt nur auf die von ihm selbst erstellte Warteschlange hat. In der globalen gleichzeitigen Warteschlange und der seriellen Warteschlange ist der Effekt der gleiche wie „dispatch_sync“.
„dispatch_barrier_async“ verwendet die Barrier-Task-Methode „Dispatch Barrier“, um den Deadlock zu lösen, der auftritt, wenn mehrere Threads gleichzeitig dieselbe Ressource lesen und schreiben.

//使用dispatch_queue_create初始化一个并发队列。第一个参数遵循反向DNS命名习惯,方便描述,第二个参数是指出是并发还是顺序。
private let concurrentPhotoQueue = dispatch_queue_create(
"com.raywenderlich.GooglyPuff.photoQueue", DISPATCH_QUEUE_CONCURRENT)

func addPhoto(photo: Photo) {
     dispatch_barrier_async(concurrentPhotoQueue) { 
        // 将写操作加入到自定义的队列。开始执行时这个就是队列中唯一的一个在执行的任务。
        self._photos.append(photo) 
        // barrier能够保障不会和其他任务同时进行。
        dispatch_async(GlobalMainQueue) { 
        // 涉及到UI所以这个通知应该在主线程中,所以分派另一个异步任务到主队列中。
               self.postContentAddedNotification()
          }
     }
}

//上面是解决了写可能发生死锁,下面是使用dispatch_sync解决读时可能会发生的死锁。
var photos: [Photo] {
     var photosCopy: [Photo]!
     dispatch_sync(concurrentPhotoQueue) { 
     //同步调度到concurrentPhotoQueue队列执行读操作
        photosCopy = self._photos // 保存
     }
     return photosCopy
}
//这样读写问题都解决了。都用异步处理避免死锁,
//异步的缺点在于调试不方便,但是比起同步容易产生死锁这个副作用还算小的。

Versandgruppe

Nachdem die mehreren zur Versandwarteschlange hinzugefügten Aufgaben verarbeitet wurden, möchten Sie die Endverarbeitung durchführen, was häufig vorkommt. Wenn Sie nur eine serielle Versandwarteschlange (serielle Warteschlange) verwenden, müssen Sie nur die gesamte Verarbeitung, die Sie ausführen möchten, zur seriellen Warteschlange hinzufügen und die Verarbeitung am Ende beenden. Bei Verwendung der gleichzeitigen Warteschlange können Sie jedoch mehrere Versandwarteschlangen gleichzeitig verwenden, wodurch der Quellcode sehr kompliziert wird.

In diesem Fall kann die Dispatch-Gruppe verwendet werden.

 *
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_async(group, queue, ^{
        NSLog(@"1");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"2");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"3");
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"done");
    });

Die Ausführungsreihenfolge der drei Warteschlangen ist ungewiss. Nachdem die Aufgaben in den drei Warteschlangen ausgeführt wurden, wird „dispatch_group_notify“ aufgerufen, um die Aufgaben in der Warteschlange „dispatch_group_notify“ auszuführen.

Darüber hinaus kann die Funktion „dispatch_group_wait“ auch in der Dispatch-Gruppe verwendet werden, um nur auf den Abschluss aller Verarbeitungsausführungen zu warten.

*
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_async(group, queue, ^{
        NSLog(@"1");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"2");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"3");
    });
    // DISPATCH_TIME_FOREVER, 永久等待,知道Group处理结束。
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

Dispatch_sync

Der Block wird synchron ausgeführt, die Funktion kehrt nicht zurück und wartet, bis der Block ausgeführt wird. Der Compiler optimiert den Code entsprechend der tatsächlichen Situation. Daher werden Sie manchmal feststellen, dass der Block tatsächlich noch im aktuellen Thread ausgeführt wird und es keinen Sinn macht, einen neuen Thread zu generieren. In der synchronen Warteschlange kehrt die Funktion „dispatch_sync“ nicht sofort zurück, sondern blockiert den aktuellen Thread und wartet auf den Abschluss der synchronen Ausführung des Blocks.

Sobald die Funktion „dispatch_sync“ aufgerufen wird, kehrt die Funktion erst zurück, wenn die angegebene Verarbeitung ausgeführt wird. „dispatch_sync“ kann die Funktion vereinfachen und kann auch als einfache Version von „dispatch_group_wait“ bezeichnet werden.

Die tatsächliche Programmiererfahrung zeigt uns, dass wir die Verwendung von „dispatch_sync“ so weit wie möglich vermeiden sollten, da es beim Verschachteln leicht zu einem Programm-Deadlock kommen kann.

如果queue1是一个串行队列的话,这段代码立即产生死锁:

   dispatch_sync(queue1, ^{

      dispatch_sync(queue1, ^{

    ......

  });

  ......

 });

„dispatch_apply“.

Die Funktion „dispatch_apply“ ist die zugehörige API der Funktion „dispatch_sync“ und der Dispatch-Gruppe. Diese Funktion hängt den angegebenen Block entsprechend der angegebenen Häufigkeit an die angegebene Dispatch-Warteschlange an und wartet, bis alle Verarbeitungsausführungen abgeschlossen sind.

dispatch_queue_t queue = dispatch_get_global_queu(0, 0);
dispatch_apply(10, queue, ^(size_t index){
NSLog(@"%zu", index);
});
NSLog(@"done");

Das Ausführungsergebnis dieses Quellcodes:

4 3 5 0 2 1 7 6 9 8 done

Der letzte Eintrag in der Ausgabe muss an letzter Stelle stehen. Dies liegt daran, dass die Funktion „dispatch_apply“ darauf wartet, dass die gesamte Verarbeitungsausführung beendet wird

  • Der erste Parameter ist die Anzahl der Wiederholungen
  • Der zweite Parameter ist die Dispatch Queue des angehängten Objekts
  • Der dritte Parameter ist die zusätzliche Verarbeitung.

Da die Funktion „dispatch_apply“ mit der Funktion „dispatch_sync“ identisch ist, wartet sie außerdem auf das Ende der Verarbeitungsausführung. Daher wird empfohlen, die Funktion „dispatch_apply“ asynchron in der Funktion „dispatch_async“ auszuführen

*
    NSArray *array = @[@"1", @"2", @"3", @"4", @"5", @"6"];
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        dispatch_apply([array count], queue, ^(size_t index) {
            NSLog(@"%zu : %@", index, [array objectAtIndex:index]);
        });

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"currentThread = %@", [NSThread currentThread]);
            NSLog(@"done");
        });
    });

„dispatch_suspend“ / „dispatch_resume“.

Beim Hinzufügen einer großen Verarbeitungsmenge zur Dispatch-Warteschlange kann es wünschenswert sein, die hinzugefügte Verarbeitung nicht während der zusätzlichen Verarbeitung auszuführen. Wenn beispielsweise das Berechnungsergebnis vom Block abgefangen wird, wirken sich einige Verarbeitungsschritte auf das Berechnungsergebnis aus.
In diesem Fall unterbrechen Sie einfach die Dispatch-Warteschlange. Fortsetzen, wenn die Ausführung möglich ist.
Die Funktion „dispatch_suspend“ unterbricht die angegebene Dispatch-Warteschlange.

dispatch_suspend(queue);

Die Funktion „dispatch_resume“ setzt die angegebene Dispatch-Warteschlange fort.

dispatch_resume(queue);

Diese Funktionen haben keine Auswirkung auf bereits durchgeführte Verarbeitungen. Nach dem Anhalten stoppt die Verarbeitung, die an die Dispatch-Warteschlange angehängt, aber noch nicht ausgeführt wurde, danach die Ausführung. Durch die Wiederherstellung können diese Prozesse fortgesetzt werden.

Versandsemaphor

„dispatch_semaphore_t“ ähnelt einem Semaphor, mit dem die Anzahl der Zugriffe auf eine bestimmte Ressource gesteuert werden kann.
Verwenden Sie den Prozess:
- Erstellen Sie zunächst ein Dispatch-Semaphore-Objekt und verwenden Sie dabei einen ganzzahligen Wert, um die verfügbare Anzahl an Ressourcen darzustellen

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  • Rufen Sie in jeder Aufgabe „dispatch_semaphore_wait“ auf, um auf den
    Betrieb der Ressourcen zu warten
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  • Rufen Sie nach dem Vorgang „dispatch_semaphore_signal“ auf, um Ressourcen freizugeben
dispatch_semaphore_signal(semaphore);

Das Folgende ist ein einfaches Beispiel für die Gewährleistung der Thread-Datensicherheit durch Semaphor:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    __block NSString *strTest = @"test";

    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--3-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--5-", strTest);
        dispatch_semaphore_signal(semaphore);
    });

Dadurch können wir auch die Datensicherheit des Threads gewährleisten.

E/A versenden

Wenn beim Lesen einer großen Datei die Datei in geeignete Größen unterteilt und mithilfe der Global Dispatch Queue parallel gelesen wird, sollte die Lesegeschwindigkeit viel schneller sein als die allgemeine Lesegeschwindigkeit. Was diese Funktion in GCD realisieren kann, ist Dispatch I/O und Dispatch Data.
Wenn Sie Dateien über Dispatch I/O lesen und schreiben, verwenden Sie Global Dispatch Queue, um eine Datei mit einer bestimmten Größe zu lesen/schreiben.

dispatch_async(queue, ^{ /* 读取  0     ~ 8080  字节*/ });  
dispatch_async(queue, ^{ /* 读取  8081  ~ 16383 字节*/ });  
dispatch_async(queue, ^{ /* 读取  16384 ~ 24575 字节*/ });  
dispatch_async(queue, ^{ /* 读取  24576 ~ 32767 字节*/ });  
dispatch_async(queue, ^{ /* 读取  32768 ~ 40959 字节*/ });  

Unten sehen Sie ein Beispiel für die Verwendung von Dispatch I/O und Dispatch Data in Apple. Der folgende Code stammt aus dem Quellcode der Apple System Log API

static int  
_asl_auxiliary(aslmsg msg, const charchar *title, const charchar *uti, const charchar *url, intint *out_fd)  
{  
    asl_msg_t *merged_msg;  
    asl_msg_aux_t aux;  
    asl_msg_aux_0_t aux0;  
    fileport_t fileport;  
    kern_return_t kstatus;  
    uint32_t outlen, newurllen, len, where;  
    int status, fd, fdpair[2];  
    caddr_t out, newurl;  
    dispatch_queue_t pipe_q;  
    dispatch_io_t pipe_channel;  
    dispatch_semaphore_t sem;  
    /* ..... 此处省略若干代码.....*/  

    // 创建串行队列  
    pipe_q = dispatch_queue_create("PipeQ", NULL);  
    // 创建 Dispatch I/O  
    pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){  
        close(fd);  
    });  

    *out_fd = fdpair[1];  

    // 该函数设定一次读取的大小(分割大小)  
    dispatch_io_set_low_water(pipe_channel, SIZE_MAX);  
    //  
    dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){  
        if (err == 0) // err等于0 说明读取无误  
        {  
            // 读取完“单个文件块”的大小  
            size_t len = dispatch_data_get_size(pipedata);  
            if (len > 0)  
            {  
                // 定义一个字节数组bytes  
                const charchar *bytes = NULL;  
                charchar *encoded;  

                dispatch_data_t md = dispatch_data_create_map(pipedata, (const voidvoid **)&bytes, &len);  
                encoded = asl_core_encode_buffer(bytes, len);  
                asl_set((aslmsg)merged_msg, ASL_KEY_AUX_DATA, encoded);  
                free(encoded);  
                _asl_send_message(NULL, merged_msg, -1, NULL);  
                asl_msg_release(merged_msg);  
                dispatch_release(md);  
            }  
        }  

        if (done)  
        {  
            dispatch_semaphore_signal(sem);  
            dispatch_release(pipe_channel);  
            dispatch_release(pipe_q);  
        }  
    });  
}  
  • Erstellen Sie einen geplanten E/A-Kanal

    1. Erstellt einen geplanten E/A-Kanal und ordnet ihn dem angegebenen Dateideskriptor zu.
dispatch_io_t dispatch_io_create( dispatch_io_type_t type, dispatch_fd_t fd, dispatch_queue_t queue, void (^cleanup_handler)(int error));

-----------------------------------

@Parameters

type  通道类型 (Dispatch I/O Channel Types.)

#define DISPATCH_IO_STREAM 0

读写操作按顺序依次顺序进行。在读或写开始时,操作总是在文件指针位置读或写数据。读和写操作可以在同一个信道上同时进行。

#define DISPATCH_IO_RANDOM 1

随机访问文件。读和写操作可以同时执行这种类型的通道,文件描述符必须是可寻址的。

fd 文件描述符

queue  The dispatch queue

cleanup_handler 发生错误时用来执行处理的 Block

-----------------------------------
  1. Erstellt einen geplanten E/A-Kanal mit einem zugehörigen Pfadnamen.
dispatch_io_t dispatch_io_create_with_path( dispatch_io_type_t type, const char* path, int oflag, mode_t mode, dispatch_queue_t queue, void (^cleanup_handler)(int error));
  1. Erstellen Sie einen neuen geplanten E/A-Kanal aus einem vorhandenen Kanal.
dispatch_io_t dispatch_io_create_with_io( dispatch_io_type_t type, dispatch_io_t io, dispatch_queue_t queue, void (^cleanup_handler)(int error));
  • Lese- und Schreibvorgänge

    1. Plant einen asynchronen Lesevorgang auf dem angegebenen Kanal.
void dispatch_io_read( dispatch_io_t channel, off_t offset, size_t length, dispatch_queue_t queue, dispatch_io_handler_t io_handler);

-----------------------------------
@Parameters

channel 通道

offset 对于DISPATCH_IO_RANDOM 类型的通道,此参数指定要读取的信道的偏移量。

对于DISPATCH_IO_STREAM 类型的通道,此参数将被忽略,数据从当前位置读取。

length 从通道读取的字节数。指定size_max继续读取数据直到达到一个EOF。
      如果处理程序与所做的参数设置为是的,一个空的数据对象,和一个0的错误代码,它意味着该通道达到了文件的结尾。

-----------------------------------

Die Funktion „dispatch_io_read“ verwendet die globale Dispatch-Warteschlange, um parallel mit dem Lesen zu beginnen. Immer wenn das Lesen jedes geteilten Dateiblocks endet, werden die Dispatch-Daten an den Block übergeben, der zurückgerufen wird, wenn der von der Dispatch-Funktion angegebene Lesevorgang endet.

  1. Plant einen asynchronen Schreibvorgang für den angegebenen Kanal.
void dispatch_io_write( dispatch_io_t channel, off_t offset, dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t io_handler);

Diese Funktion nimmt die angegebenen Daten und übermittelt sie an die io_handler-Blockwarteschlange, um über den Fortschritt des Vorgangs zu berichten. Wenn der done-Parameter des Handlers auf „no“ gesetzt ist, bedeutet dies, dass nur ein Teil der Daten geschrieben wird. Wenn der Parameter done auf „yes“ gesetzt ist, bedeutet dies, dass der Schreibvorgang abgeschlossen ist und der Handler keine erneute Übermittlung durchführt. Wenn die Operation erfolgreich war, wird der Fehlerparameter des Handlers auf 0 gesetzt.

  • Legen Sie die Größe eines Lesevorgangs fest
    1. Legen Sie die maximale Anzahl der gleichzeitig gelesenen Bytes fest
void dispatch_io_set_high_water( dispatch_io_t channel, size_t high_water);
  1. Legen Sie das Mindestbyte fest, das jeweils gelesen werden soll
void dispatch_io_set_low_water( dispatch_io_t channel, size_t low_water);

おすすめ

転載: blog.csdn.net/sinat_15735647/article/details/77982716