Latest web development tutorials

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.