Ruby Multithreading
Chaque programme en cours d'exécution sur le système est un processus. Chaque processus contient un ou plusieurs threads.
Un thread est un seul flux de contrôle du programme séquentiel, exécuter simultanément plusieurs threads dans un seul programme font des choses différentes, appelées multi-threading.
Ruby, nous pouvons être créé par de multiples threads classe Thread, threads Ruby sont un poids léger, il peut être un moyen efficace de mettre en œuvre le code parallèle.
Création d'un fil Ruby
Pour démarrer un nouveau thread, vous pouvez simplement appeler Thread.new:
# 线程 #1 代码部分 Thread.new { # 线程 #2 执行代码 } # 线程 #1 执行代码
Exemples
L'exemple suivant montre comment utiliser le multithreading dans le programme Ruby:
#!/usr/bin/ruby def func1 i=0 while i<=2 puts "func1 at: #{Time.now}" sleep(2) i=i+1 end end def func2 j=0 while j<=2 puts "func2 at: #{Time.now}" sleep(1) j=j+1 end end puts "Started At #{Time.now}" t1=Thread.new{func1()} t2=Thread.new{func2()} t1.join t2.join puts "End at #{Time.now}"
Le code ci-dessus est exécutée à la suite de:
Started At Wed May 14 08:21:54 -0700 2014 func1 at: Wed May 14 08:21:54 -0700 2014 func2 at: Wed May 14 08:21:54 -0700 2014 func2 at: Wed May 14 08:21:55 -0700 2014 func1 at: Wed May 14 08:21:56 -0700 2014 func2 at: Wed May 14 08:21:56 -0700 2014 func1 at: Wed May 14 08:21:58 -0700 2014 End at Wed May 14 08:22:00 -0700 2014
Cycle de vie du fil
1, créer un thread peut utiliser Thread.new, vous pouvez également utiliser le même Thread.start de syntaxe ou Thread.fork trois façons de créer un fil.
2, créer un fil sans démarrer, le fil sera exécuté automatiquement.
3, classe Thread définit des méthodes pour manipuler fil. Discussion d'exécution Thread.new blocs de code.
4, le bloc de fil est la valeur de la dernière instruction dans le fil, le fil peut être invoqué par une méthode, si le fil est terminé, il retourne la valeur du fil, ou ne retourne pas de valeur jusqu'à ce que le fil est terminé.
5, la méthode Thread.current retourne un objet pour la représentation du thread courant. méthode Thread.main renvoie le thread principal.
6, la méthode est effectuée par des fils Thread.Join, cette méthode va suspendre le thread principal jusqu'à ce que le thread en cours est terminé.
Etat Thread
Discussion a cinq états:
Etat Thread | Valeur de retour |
---|---|
Executable | course |
endormi | endormi |
quitter | abandon |
résiliation normale | faux |
Arrêt anormal se produit | néant |
Fils et aberrante
Lorsqu'une exception de fil se produit, et pas de sauvetage a été capturé, le fil devrait normalement être résilié sans préavis. Toutefois, si d'autres threads discussion # rejoignent parce que la relation a été en attente pour ce thread, les threads en attente seront également soulevé la même exception.
begin t = Thread.new do Thread.pass # 主线程确实在等join raise "unhandled exception" end t.join rescue p $! # => "unhandled exception" end
Utilisez les trois méthodes suivantes, vous pouvez obtenir l'interprète d'interrompre le fonctionnement lorsqu'un thread se termine en raison d'une exception.
- Script de démarrage spécifie option-d, et le fonctionnement du mode de débogage.
- Avec
Thread.abort_on_exception
mis le drapeau. - Utilisez
Thread#abort_on_exception
jeu de thread spécifié drapeau.
Lorsque vous utilisez l'une des trois méthodes décrites ci-dessus, l'ensemble interprète sera interrompue.
t = Thread.new { ... } t.abort_on_exception = true
Synchronisation Thread
En Ruby, fournit trois de manière synchronisée, à savoir:
1. classe Mutex implémente la synchronisation des threads
2. réglementation de transfert de données de classe Queue mettre en œuvre la synchronisation des threads
3. Utilisez ConditionVariable commande de synchronisation
Par Mutex classe implémente la synchronisation des threads
Par classe Mutex implémente le contrôle de la synchronisation des threads, si vous avez besoin également une horloge variable de programme dans plusieurs threads, vous pouvez utiliser le cadenas pour verrouiller la partie variable. Le code se présente comme suit:
#!/usr/bin/ruby require "thread" puts "Synchronize Thread" @num=200 @mutex=Mutex.new def buyTicket(num) @mutex.lock if @num>=num @num=@num-num puts "you have successfully bought #{num} tickets" else puts "sorry,no enough tickets" end @mutex.unlock end ticket1=Thread.new 10 do 10.times do |value| ticketNum=15 buyTicket(ticketNum) sleep 0.01 end end ticket2=Thread.new 10 do 10.times do |value| ticketNum=20 buyTicket(ticketNum) sleep 0.01 end end sleep 1 ticket1.join ticket2.join
Le code ci-dessus est exécutée à la suite de:
Synchronize Thread you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets you have successfully bought 20 tickets you have successfully bought 15 tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets sorry,no enough tickets
Outre l'utilisation de verrouillage verrouillé variable, vous pouvez également utiliser try_lock variable verrouillée, vous pouvez également utiliser Mutex.synchronize synchroniser l'accès à une variable particulière.
transfert de données de réglementation de la classe Queue implémente la synchronisation des threads
classe de file d'attente qui représente une file d'attente de support de fil, la file d'attente peut être synchronisé à la fin de la visite. Différents threads peuvent utiliser une classe unifiée, mais ne vous inquiétez pas de savoir si les données peuvent être synchronisées dans cette file d'attente, en plus de limiter l'utilisation de SizedQueue longueur de la queue de classe
classe SizedQueue peut être très pratique pour nous aider à développer des applications filetées pour synchroniser, les problèmes de synchronisation devraient être ajoutés à la longue file d'attente, vous ne vous souciez pas de threads.
les producteurs et les consommateurs classiques:
#!/usr/bin/ruby require "thread" puts "SizedQuee Test" queue = Queue.new producer = Thread.new do 10.times do |i| sleep rand(i) # 让线程睡眠一段时间 queue << i puts "#{i} produced" end end consumer = Thread.new do 10.times do |i| value = queue.pop sleep rand(i/2) puts "consumed #{value}" end end consumer.join
Sortie du programme:
SizedQuee Test 0 produced 1 produced consumed 0 2 produced consumed 1 consumed 2 3 produced consumed 34 produced consumed 4 5 produced consumed 5 6 produced consumed 6 7 produced consumed 7 8 produced 9 produced consumed 8 consumed 9
Variables de la discussion
Discussion peut avoir sa variable privée, une variable privée fil de l'écriture de fil lorsque le fil est créé. Il peut être utilisé dans le cadre du fil, mais le fil ne peut pas être partagé à l'extérieur.
Mais parfois, les variables locales thread ne nécessitent pas un autre thread ou le thread principal pour accéder à la façon de faire? ruby leur permet de créer un fil est fourni par le nom de la variable, le fil est considéré comme le même style de table de hachage de hachage. Par [] = écrit par [] lire les données. Nous regardons le code suivant:
#!/usr/bin/ruby count = 0 arr = [] 10.times do |i| arr[i] = Thread.new { sleep(rand(0)/10.0) Thread.current["mycount"] = count count += 1 } end arr.each {|t| t.join; print t["mycount"], ", " } puts "count = #{count}"
Le code ci-dessus est exécuté sortie est:
8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10
Les thread principal attend pour l'exécution du thread enfant est terminé, puis sorties chaque valeur. .
priorité Thread
La priorité de cette discussion est le principal facteur affectant la planification des threads. D'autres facteurs incluent la longueur de temps pour effectuer CPU-fil ordonnancement de paquets et ainsi de suite.
Vous pouvez utiliser Thread.priority obtenu la priorité d'un fil et utiliser Thread.priority = méthode pour ajuster la priorité du fil.
Les valeurs par défaut de priorité d'un thread à 0. Faster exécution de priorité plus élevée.
Une discussion peut accéder à toutes les données dans le cadre de leur propre, mais s'il y a d'autres threads doivent accéder à des données dans un thread devrait être comment le faire? classe Thread fournit des données de fil l'accès de l'autre, vous pouvez simplement mettre un fil comme une table de hachage, peut être utilisé dans tous les threads [] data = Write, utilisez [] lire les données.
athr = Thread.new { Thread.current["name"] = "Thread A"; Thread.stop } bthr = Thread.new { Thread.current["name"] = "Thread B"; Thread.stop } cthr = Thread.new { Thread.current["name"] = "Thread C"; Thread.stop } Thread.list.each {|x| puts "#{x.inspect}: #{x["name"]}" }
Nous pouvons voir, le fil comme une table de hachage, utilisez [] et [] = méthode, on atteint le partage des données entre les threads.
mutex Thread
Mutex (Exclusion Mutal = mutex) est une méthode de programmation multi-thread, un mécanisme pour empêcher deux threads simultanément pour les mêmes ressources publiques (comme les variables globales) lire et écrire.
Des exemples de non-utilisation Mutax
#!/usr/bin/ruby require 'thread' count1 = count2 = 0 difference = 0 counter = Thread.new do loop do count1 += 1 count2 += 1 end end spy = Thread.new do loop do difference += (count1 - count2).abs end end sleep 1 puts "count1 : #{count1}" puts "count2 : #{count2}" puts "difference : #{difference}"
Exécutez l'exemple ci-dessus est sortie:
count1 : 9712487 count2 : 12501239 difference : 0
Exemple de Mutax
#!/usr/bin/ruby require 'thread' mutex = Mutex.new count1 = count2 = 0 difference = 0 counter = Thread.new do loop do mutex.synchronize do count1 += 1 count2 += 1 end end end spy = Thread.new do loop do mutex.synchronize do difference += (count1 - count2).abs end end end sleep 1 mutex.lock puts "count1 : #{count1}" puts "count2 : #{count2}" puts "difference : #{difference}"
Exécutez l'exemple ci-dessus est sortie:
count1 : 1336406 count2 : 1336406 difference : 0
impasse
Plus de deux unités d'exploitation, les deux parties sont en attente de l'autre pour arrêter la course, pour obtenir les ressources système, mais pas une sortie précoce du parti, cette situation est appelée impasse.
Par exemple, un processus p1 prend affichage, alors que vous devez utiliser l'imprimante, et l'imprimante est occupée par le processus p2, p2 doit également utiliser le moniteur, formant ainsi une impasse.
Lorsque nous utilisons Mutex objet a besoin d'attention fil impasse.
Exemples
#!/usr/bin/ruby require 'thread' mutex = Mutex.new cv = ConditionVariable.new a = Thread.new { mutex.synchronize { puts "A: I have critical section, but will wait for cv" cv.wait(mutex) puts "A: I have critical section again! I rule!" } } puts "(Later, back at the ranch...)" b = Thread.new { mutex.synchronize { puts "B: Now I am critical, but am done with cv" cv.signal puts "B: I am still critical, finishing up" } } a.join b.join
L'exemple ci-dessus sortie est:
A: I have critical section, but will wait for cv (Later, back at the ranch...) B: Now I am critical, but am done with cv B: I am still critical, finishing up A: I have critical section again! I rule!
méthode de classe Thread
Thread (thread) méthode de classe complète comme suit:
Non. | Méthode description |
---|---|
1 | Thread.abort_on_exception S'il est vrai, une fois par thread se termine en raison d'une exception, l'ensemble interprète sera interrompue. La valeur par défaut est faux, qui est, dans des circonstances normales, si une exception de thread se produit et l'exception ne Thread # rejoindre et autres détecté, le fil sera mis fin sans avertissement. |
2 | Thread.abort_on_exception = Sivrai,une fois par thread se termine en raison d'une exception, l'ensemble interprète sera interrompue. Retourne un nouvel état |
3 | Thread.critical Renvoie une valeur booléenne. |
4 | Thread.critical = Lorsque la valeur est vrai, le fil ne sera pas activé. Si le thread courant pour accrocher (arrêt) ou (signal) intervention, sa valeur sera automatiquement changée en faux. |
5 | Thread.current Retourne le fil conducteur de courant (le thread en cours). |
6 | Thread.exit Elle se termine le thread courant. Renvoie le thread en cours. Si le thread courant est le seul fil, en utilisant la sortie (0) de mettre fin à son fonctionnement. |
7 | Thread.fork {block} Comme avec Thread.new générer des threads. |
8 | Thread.kill (AThread) Termine le fil en cours d'exécution. |
9 | Thread.list Retourne un tableau de fil en direct est en cours d'exécution ou de l'état suspendu. |
10 | Thread.main Retour au thread principal. |
11 | Thread.new ([arg] *) { | args | block} Générer fil et commencer l'exécution. Le nombre sera passé intact au bloc. Cela peut démarrer un thread dans le même temps, la valeur sera transmise aux variables locales inhérentes au fil. |
12 | Thread.pass Le droit d'exécuter d'autres threads. Il ne change pas l'état des threads en cours d'exécution, mais remettra le contrôle d'autres threads peuvent exécuter (ordonnancement des threads Explicit). |
13 | Thread.start ([args] *) { | args | block} Générer fil et commencer l'exécution. Le nombre sera passé intact au bloc. Cela peut démarrer un thread dans le même temps, la valeur sera transmise aux variables locales inhérentes au fil. |
14 | Thread.stop Le thread en cours est suspendu jusqu'à ce que les autres threads méthode run se réveillent à nouveau le fil. |
méthode d'instance de discussion
L'exemple suivant appelle la méthode d'instance de fil se joindre à:
#!/usr/bin/ruby thr = Thread.new do # 实例化 puts "In second thread" raise "Raise exception" end thr.join # 调用实例化方法 join
Voici une liste complète des exemples de la méthode:
Non. | Méthode description |
---|---|
1 | thr [nom] Retirez le fil du nom correspondant aux données inhérentes. nom peut être une chaîne ou un symbole. Si le nom ne correspond pas aux données, elle renvoie zéro. |
2 | thr [name] = Réglez la valeur du nom de fil dans les données caractéristiques correspondantes, le nom peut être une chaîne ou un symbole. Si elle est définie à zéro, supprime les données correspondantes dans ce fil. |
3 | thr.abort_on_exception Renvoie une valeur booléenne. |
4 | thr.abort_on_exception = Si sa valeur est vrai, alors une fois par thread se termine en raison d'une exception, l'ensemble interprète sera interrompue. |
5 | thr.alive? Si le fil est "live", il retourne vrai. |
6 | thr.exit Termine le fil en cours d'exécution. Renvoie l'auto. |
7 | thr.join Suspend le thread courant jusqu'à ce que le self run jusqu'à ce que le thread se termine. Si l'auto à cause de fin anormale, le thread courant déclenchera la même exception. |
8 | thr.key? Si le nom correspondant aux données inhérentes a été threads définis, puis renvoie true |
9 | thr.kill Thread.exit similaires. |
10 | thr.priority Renvoie la priorité du fil. La priorité par défaut est 0. Plus la valeur est élevée, plus la priorité. |
11 | thr.priority = Réglage de la priorité de thread. Vous pouvez également le mettre à négatif. |
12 | thr.raise (uneException) Dans ce fil jeté de force. |
13 | thr.run Redémarrez en attente (arrêt) fil. La différence est que le réveil effectuera fil de commutation immédiatement. Si vous utilisez cette méthode pour traiter les morts ressusciteront ThreadError exception. |
14 | thr.safe_level Renvoie le niveau de sécurité de l'auto. Safe_level le thread courant $ SÛR même. |
15 | thr.status Utilisation de la chaîne "run", "sommeil" ou "abandon" pour indiquer l'état du fil en direct si un thread se termine normalement, puis elle retourne false. Ruoyin fin anormale, il renvoie nil. |
16 | thr.stop? Si le fil est mis fin à l'état (mort) ou de suspendre (arrêt), le retour vrai. |
17 | thr.value Attendez jusqu'à ce que le fil de l'auto se termine (équivalent à joindre), le retour de la valeur du bloc du fil si les threads en cours d'exécution se produisent pendant anormale, l'exception sera soulevée à nouveau. |
18 | thr.wakeup L'état est suspendu (arrêt) de fil à l'état prêt (run), si la méthode est effectuée sur le fil mort soulèvera ThreadError exception. |