web-dev-qa-db-fra.com

Différence entre DispatchSourceTimer, Timer et asyncAfter?

J'ai du mal à comprendre les principales différences entre DispatchSourceTimer , Timer et asyncAfter ( dans mon cas pour la planification une tâche qui doit être exécutée toutes les X secondes, bien que la compréhension des différences de temporisation puisse être utile pour ) (Ou existe-t-il un autre mécanisme de planification (plus efficace) dans Swift en plus des minuteries listées?).

Un Timer a besoin d'une boucle d'exécution active sur la file d'attente actuelle sur laquelle il a été démarré. Un DispatchSourceTimer n'en a pas besoin. Un Timer empêche le CPU de se mettre en veille. Est-ce que cela s'applique également à DispatchSourceTimer/asyncAfter?

Dans quelle situation un Timer est préféré à un DispatchSourceTimer/asyncAfter? Et bien sûr, la différence entre chacun d'eux?

Je souhaite planifier le travail toutes les 15 secondes dans mon application sur une file d'attente privée. Cela signifie que je dois utiliser DispatchSourceTimer car je suis dans une file d'attente qui n'est pas le thread principal (ou ajouter une boucle d'exécution à la file d'attente et utiliser Timer). Cependant, je ne vois aucun avantage à utiliser un Timer en premier lieu. Peut-être qu'il y a une autre opération que je peux utiliser ce travail de planification toutes les X secondes sur une file d'attente privée qui est plus efficace qu'un DispatchSourceTimer, mais je n'ai pas trouvé de meilleure solution.

Un DispatchSourceTimer est-il plus efficace qu'un Timer? Ou dois-je utiliser une méthode auto-appelante avec asyncAfter?

Ceci est le code pour créer les minuteries.

asyncAfter

DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(2)) {
    // Code
}

Minuterie

Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
    // Code
}

DispatchSourceTimer

let timer = DispatchSource.makeTimerSource()

timer.schedule(deadline: .now() + .seconds(1))

timer.setEventHandler {
    // Code
}

timer.activate()

Quels sont les inconvénients et les avantages de tous les chronomètres? Quand dois-je utiliser l'un au-dessus de l'autre? De quelle manière la minuterie est-elle la plus efficace? J'ai trouvé ce qui suit:

Minuteur

Avantages:

  • Peut être invalidé
  • Aucune référence nécessaire
  • Peut être arrêté pendant sa programmation.

Les inconvénients:

  • Empêche le processeur de rester inactif
  • Doit être exécuté sur une file d'attente avec une boucle d'exécution (sinon rien ne se passe, même pas de déclencheur d'assertion ...)

DispatchSourceTimer

Avantages:

  • Peut être annulé
  • Aucune boucle d'exécution nécessaire

Les inconvénients:

  • Besoin d'une référence solide sinon il est désalloué instantanément

asyncAfter

Avantages: - Pas de boucle d'exécution nécessaire

Inconvénients: - Ne peut pas être annulé (je pense)

Y a-t-il encore plus de minuteries? Pourquoi y a-t-il tant de minuteries? Je m'attendais à une réelle différence entre les différents chronomètres, mais je ne les ai pas trouvés.

Beaucoup de questions ici comme vous pouvez le lire. La question principale est: quels minuteries sont disponibles et quels minuteries dois-je utiliser dans quel cas et pourquoi?

25
J. Doe

Le minuteur est un pont Swift de NSTimer, qui remonte à NeXTSTEP, longtemps, longtemps avant Grand Central Dispatch (GCD) et des choses comme DispatchSourceTimer, qui ne sont pas arrivées avant 10.6 (sous la forme de dispatch_source_set_timer) et dispatchAfter (sous la forme de dispatch_after).

NSTimer est basé sur la boucle d'exécution, qui était le principal moyen utilisé pour la concurrence jusqu'à GCD. Il s'agit d'un système de concurrence coopératif, conçu principalement pour fonctionner sur un seul thread sur un seul cœur (bien qu'il puisse être étendu à des environnements multithreads).

Bien que la boucle d'exécution soit toujours très importante dans Cocoa, elle n'est plus le moyen principal, ni même préféré, de gérer la concurrence. Depuis 10.6, GCD a été l'approche de plus en plus préférée (bien que l'ajout d'une API NSTimer basée sur des blocs dans la période 10.12 soit une modernisation bienvenue).

Sur une échelle de 15 secondes, les différences d'efficacité sont peu pertinentes. Cela dit, je ne comprends pas votre commentaire "Un temporisateur empêche le CPU de passer en mode veille". Je ne crois pas que ce soit vrai. Le CPU restera définitivement dans l'état inactif en attendant qu'un NSTimer se déclenche.

Je ne mettrais pas en place une boucle d'exécution juste pour exécuter un NSTimer. Il serait préférable de le programmer sur la boucle d'exécution principale, puis d'utiliser DispatchQueue.async pour effectuer le travail réel sur une autre file d'attente.

En règle générale, j'utilise l'outil de plus haut niveau qui répond au besoin. Ce sont ceux qui Apple est susceptible d'optimiser le meilleur au fil du temps avec moi en faisant le moins de changements. Par exemple, les dates d'incendie NSTimer sont automatiquement ajustées pour améliorer l'efficacité énergétique. Avec DispatchSourceTimer, vous obtenez le contrôle sur le paramètre leeway pour obtenir le même avantage, mais c'est à vous de le définir (la valeur par défaut est zéro, ce qui a le pire impact énergétique). Bien sûr, l'inverse est également vrai. DispatchSourceTimer est le plus bas niveau et vous donne le plus de contrôle, donc si c'est ce dont vous avez besoin, c'est celui à utiliser.

Pour votre exemple, j'utiliserais probablement un temporisateur et je répartirais simplement dans la file d'attente privée dans le cadre du bloc. Mais un DispatchSourceTimer serait tout à fait approprié.

asyncAfter est vraiment une chose différente, car il s'agit toujours d'un seul coup. C'est génial si vous voulez un one-shot, mais cela change les choses si vous voulez répéter. Si vous appelez simplement asyncAfter dans le bloc pour répéter, cela va être 15 secondes après la dernière fois que vous avez terminé, plutôt que d'être espacés de 15 secondes. Les premiers auront tendance à dériver un peu tard dans le temps. La question de conception est la suivante: si, pour une raison quelconque, votre tâche a pris 5 secondes, voudriez-vous que le prochain événement d'incendie se produise 15 secondes après la fin de celui-ci, ou souhaitez-vous une constante de 15 secondes entre chaque événement d'incendie? Votre choix déterminera quel outil est correct.

Comme une petite note là-bas, les événements NSTimer sont toujours un peu plus tard que prévu. Les événements GCD avec une marge de manœuvre peuvent être un peu tôt ou un peu tard. En pratique, il n'y a rien de tel que d'être "à l'heure" (c'est une période de longueur nulle; vous n'allez pas la frapper). Donc, la question est toujours de savoir si vous êtes promis pour être en retard comme NSTimer, ou vous pourriez être en avance comme GCD avec une marge de manœuvre.

37
Rob Napier