fibre/fibre
Dans le système d'exploitation, en plus des processus et des threads, il existe également un type de fibre (fibre, également appelée coroutine) qui est rarement utilisée. Les fibres sont souvent comparées aux threads pour le système d’exploitation, ce sont deux états d’exécution légers. Les fibres sont généralement considérées comme plus légères et ont moins de surcharge que les fils. La différence est que les fibres sont créées par des threads ou des fibres et que la planification des fibres est entièrement contrôlée par le code utilisateur. Pour le noyau du système, il s'agit d'une méthode de planification non préemptive. Les threads et les processus sont planifiés en coopération. le noyau et implémenter le multitâche préemptif en fonction de la priorité. De plus, le noyau du système ne connaît pas l'état de fonctionnement spécifique des fibres, et l'utilisation des fibres est en fait relativement indépendante du système d'exploitation.
Dans le nœud, un seul thread est uniquement destiné au javascript et sa couche inférieure est en fait pleine de multi-threads. Si vous devez implémenter le multithreading en JavaScript, une approche courante consiste à écrire un module complémentaire C pour contourner le mécanisme monothread de JavaScript. Cependant, cette méthode augmente la difficulté et le coût du développement et du débogage. Comme beaucoup d’autres langages de script, nous pouvons également introduire le concept de fibres dans node.
fibres-nœuds
La bibliothèque node-fibers fournit des fonctions de fibre pour les nœuds. Le test multithread n'a pas produit de résultats idéaux, mais il a un effet significatif sur la conversion de l'asynchrone en synchrone et peut également être utile pour réduire les piles d'appels de nœuds et la récursivité infinie. Ce document présente principalement comment utiliser la bibliothèque node-fibers et la conversion asynchrone vers synchrone.
Installer
les node-fibers sont écrits en langage C. Le téléchargement direct du code source nécessite une compilation. Habituellement, vous pouvez l'installer directement avec npm :
Utilisation de la bibliothèque de fibres
API
1.Fibre(fn)/nouvelle fibre(fn) :
Créez une fibre, qui peut être utilisée comme constructeur ou appelée comme fonction ordinaire. Par exemple :
Lorsque run() est appelé, la fibre démarre et alloue une nouvelle pile pour fn qui s'exécutera sur cette nouvelle pile jusqu'à ce que fn ait une valeur de retour ou que rendement() soit appelé. Après que fn ait renvoyé ou appelé rendement(), la pile est réinitialisée. Lorsque run() est à nouveau appelé, la fibre sera redémarrée et fn s'exécutera dans la pile allouée pour la première fois.
2.Fibre.courant :
Obtenez la fibre actuelle et exploitez-la. Si vous spécifiez une variable à lui associer, assurez-vous que cette fibre peut être libérée, sinon le mécanisme de garbage collection de V8 ignorera toujours cette partie de la mémoire, provoquant des fuites de mémoire.
3.Fiber.yield(param):
Cette fonction a été mentionnée dans la description précédente. La méthode rendement() est utilisée pour interrompre la fibre, similaire au retour dans une certaine mesure. Une fois rendement() exécuté, le code suivant dans cette fibre n'aura aucune chance de s'exécuter, par exemple :
Après l'exécution, seul "Fiber Start" sera affiché, et cette dernière commande de sortie ne sera pas exécutée. Si un paramètre est passé à rendement(), alors ce paramètre est utilisé comme valeur de retour de run().
4.Fiber.prototype.run(param):
Cette méthode est déjà très familière. Il existe deux temps pour appeler run() mentionnés précédemment, l'un lorsque la fibre n'est pas démarrée et l'autre lorsque la fibre est libérée. Le comportement de run() n’est pas le même à ces deux temps.
Lorsque Fiber n'est pas démarré, run() accepte un argument et le transmet à fn comme argument. Lorsque Fiber gère l'état de rendement, run() accepte un paramètre et l'utilise comme valeur de retour de rendement() qui ne s'exécutera pas depuis le début, mais continuera à s'exécuter à partir du point d'interruption. La relation entre les paramètres et les valeurs de retour de fn, rendement et run peut être expliquée à travers le petit exemple suivant :
Le résultat est le suivant :
D'après l'exemple ci-dessus, il est évident que l'utilisation de rendement est assez différente de la syntaxe JavaScript actuelle. Le mot-clé rendement a été implémenté dans d'autres langages (C#, Python, etc.) comme interruption pour les itérateurs. Vous pourriez aussi bien implémenter un itérateur sur le nœud et découvrir l'utilisation de rendement en détail. Prenons comme exemple la séquence de Fibonacci du début :
Le résultat est :
Il y a deux problèmes auxquels il faut prêter attention. Premièrement, le rendement est considéré comme une méthode, plutôt comme un mot-clé. Contrairement à run, le rendement n'a pas besoin de s'appuyer sur une instance Fibre, contrairement à run. Si vous appelez run dans Fiber, vous devez utiliser : Fiber.current.run(); deuxièmement, rendement lui-même est un mot-clé réservé de JavaScript. On ne sait pas s'il sera activé et quand, le code peut donc subir des modifications dans le fichier. avenir. .
5.Fiber.prototype.reset() :
Nous savons déjà que Fiber peut avoir des temps différents, ce qui affectera également le comportement de course. La méthode de réinitialisation revient à l’état initial quel que soit l’état traité par Fiber. L'exécution ultérieure de run réexécutera fn.
6.Fiber.prototype.throwInto(Exception) :
Essentiellement, throwInto lèvera l'exception qui lui est transmise et utilisera les informations d'exception comme valeur de retour de run. Si l’exception générée n’est pas gérée dans Fiber, l’exception continuera à apparaître. Que l'exception soit gérée ou non, elle forcera le rendement et interrompra la fibre.
Utilisation de la future bibliothèque
Il n'est pas toujours raisonnable d'utiliser Fiber directement dans le nœud, car l'API de Fiber est vraiment simple, en utilisation réelle, elle produira inévitablement du code répétitif et long, ce qui n'est pas propice à la maintenance. Il est recommandé d'ajouter une couche d'abstraction entre le nœud et la fibre pour permettre à la fibre de mieux fonctionner. La future bibliothèque propose une telle abstraction. La future bibliothèque ou tout niveau d'abstraction n'est peut-être pas parfait. Personne n'a raison ou tort, seulement ce qui est applicable ou non. Par exemple, la future bibliothèque nous fournit une API simple qui peut compléter le travail d'asynchrone à synchrone, mais elle ne peut rien faire pour encapsuler le générateur (similaire au générateur de séquence de Fibonacci ci-dessus).
La future bibliothèque n'a pas besoin d'être téléchargée et installée séparément. Elle est déjà incluse dans la bibliothèque de fibres. Lorsque vous l'utilisez, vous n'avez besoin que de var future=require('fibers/future').
API
1.Function.prototype.future() :
Ajout d'une méthode future au type de fonction, convertissant la fonction en une "fonction funture".
En fait, la méthode power est exécutée dans Fibel. Cependant, la version existante de future comporte des bugs et il n'y a pas d'explication officielle officielle. Si vous devez utiliser cette fonction, veuillez supprimer les lignes 339 et 350 de future.js.
2.nouveau futur()
Constructeur d'objet Future, détaillé ci-dessous.
3.Future.wrap(fn, idx)
La méthode wrap encapsule le fonctionnement asynchrone à synchrone et est la méthode la plus précieuse pour nous dans la future bibliothèque. fn représente la fonction qui doit être convertie, idx représente le nombre de paramètres acceptés par fn, et sa méthode de rappel est considérée comme le dernier paramètre (la formulation de l'API ici est assez controversée. Certaines personnes ont tendance à passer la position où le rappel devrait l'être. Heureusement, la méthode wrap est relativement simple et peut être plus facile à modifier le code). Vous pouvez comprendre l'utilisation de wrap en regardant un exemple :
À partir de cet exemple, nous pouvons voir que la conversion fibre asynchrone vers synchrone est en effet très efficace À l'exception de l'étape supplémentaire .wait() dans la syntaxe, les autres méthodes fs.readFileSync déjà fournies par fs sont les mêmes.
4.Future.wait(futures) :
Cette méthode a déjà été vue à plusieurs reprises. Comme son nom l’indique, sa fonction est d’attendre les résultats. Si vous souhaitez attendre le résultat d'une instance future, appelez simplement futureInstance.wait() directement ; si vous devez attendre le résultat d'une série d'instances futures, appelez Future.wait(futuresArray). Il convient de noter que dans la deuxième utilisation, si une erreur se produit lors de l'exécution d'une instance future, la méthode wait ne générera pas d'erreur, mais nous pouvons utiliser la méthode get() pour obtenir directement le résultat en cours d'exécution.
5.Future.prototype.get() :
L'utilisation de get() est très similaire à la première méthode de wait(). La différence est que get() renvoie le résultat immédiatement. Si les données ne sont pas prêtes, get() générera une erreur.
6.Future.prototype.resolve(param1,param2):
La méthode wrap ci-dessus donne toujours aux gens l'impression que le futur avale en fait la fonction de rappel de la méthode asynchrone et renvoie directement le résultat asynchrone. En fait, future fournit également une solution pour définir des fonctions de rappel via la méthode de résolution. solve accepte jusqu'à deux paramètres. Si un seul paramètre est transmis, le futur pensera qu'une fonction de rappel de style nœud est transmise, comme dans l'exemple suivant :
如果傳入兩個參數,表示錯誤和資料分別做處理,範例如下:
另外 future並不區分 resolve的呼叫時機,如果資料還沒準備好,則將回調函數壓入佇列,由 resolver()方法統一調度,否則直接取資料立即執行回調函數。
7.Future.prototype.isResolved():
傳回布林值,表示操作是否已執行。
8.Future.prototype.proxy(futureInstance):
proxy方法提供一種 future實例的代理,本質上是對 resolve方法的包裝,其實是將一個instance的回調方法作為另一個instance的回呼執行者。例如:
雖然執行的是 proxy,但是最終 target的回調函數執行了,並且是以 proxy的執行結果驅動 target的回調函數。這種代理手段也許在我們的實際應用上有很大作用,我暫時還沒有深入地思考過。
9.Future.prototype.return(value):
10.Future.prototype.throw(error):
11.Future.prototype.resolver():
12.Future.prototype.detach():
以上四個API呢我覺得相對於別的API,實際使用的場景或效果比較一般。 return和 throw都受 resolver方法調度,這三個方法都很重要,在正常的future使用流程中都會默默工作著,只是我沒有想出具體單獨使用它們的場景,所以沒有辦法具體介紹。 detach方法只能算 resolve方法的簡化版,亦沒有介紹的必要。