English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。
线程是程序中的一个单一顺序控制流程,在单个程序中同时运行多个线程以完成不同的工作,称为多线程。
在Ruby中,我们可以通过 Thread 类来创建多线程,Ruby的线程是轻量级的,可以以高效的方式实现并行代码。
要启动一个新的线程,只需要调用 Thread.new 即可:
# 线程 #1 代码部分 Thread.new { # 线程 #2 执行代码 } # 线程 #1 执行代码
以下示例展示了如何在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}" 1=Thread.new{func1} 2=Thread.new{func2} 1.join 2.join t
Das Ausgaberesultat des obigen Codes ist:
puts "End at #{Time.now}" 14 08:21:54 -0700 2014 Started At Wed May1 func 14 08:21:54 -0700 2014 Started At Wed May2 func 14 08:21:54 -0700 2014 Started At Wed May2 func 14 08:21:55 -0700 2014 Started At Wed May1 func 14 08:21:56 -0700 2014 Started At Wed May2 func 14 08:21:56 -0700 2014 Started At Wed May1 func 14 08:21:58 -0700 2014 at: Wed May 14 08:22End at Wed May -0700 2014
1线程生命周期
2、线程的创建可以使用Thread.new,同样可以以同样的语法使用Thread.start 或者Thread.fork这三个方法来创建线程。
3、创建线程后无需启动,线程会自动执行。
4、Thread 类定义了一些方法来操控线程。线程执行Thread.new中的代码块。
5、线程代码块中最后一个语句是线程的值,可以通过线程的方法来调用,如果线程执行完毕,则返回线程值,否则不返回值直到线程执行完毕。
6、Thread.current 方法返回表示当前线程的对象。 Thread.main 方法返回主线程。
、通过 Thread.Join 方法来执行线程,这个方法会挂起主线程,直到当前线程执行完毕。5有
线程状态 | 返回值 |
---|---|
可执行 | run |
睡眠 | Sleeping |
退出 | aborting |
正常终止 | false |
发生异常终止 | nil |
当某线程发生异常,且没有被rescue捕捉到时,该线程通常会被无警告地终止。但是,若有其他线程因为Thread#join的关系一直等待该线程的话,则等待的线程同样会被引发相同的异常。
begin t = Thread.new do Thread.pass # 主线程确实在等join raise "unhandled exception" end t.join rescue p $! # => "unhandled exception" end
使用下列3个方法,就可以让解释器在某个线程因异常而终止时中断运行。
启动脚本时指定-d选项,并以调试模式运行。
用Thread.abort_on_exception设置标志。
使用Thread#abort_on_exception对指定的线程设定标志。
当使用上述3这种方法之一后,整个解释器就会被中断。
t = Thread.new { ... } t.abort_on_exception = true
在Ruby中,提供三种实现同步的方式,分别是:
1通过Mutex类实现线程同步
2Überwachen Sie die Queue-Klasse, um die Synchronisation der Datenübergabe zu realisieren
3Verwenden Sie ConditionVariable, um die Synchronisation zu steuern
Durch die Klasse Mutex wird die Synchronisation der Threads realisiert. Wenn mehrere Threads gleichzeitig auf eine Programmvariable zugreifen müssen, kann ein Teil dieser Variable mit lock gesperrt werden. Code wie folgt:
#!/usr/bin/ruby require "thread" puts "Synchronisieren Sie den Thread" @num=200 @mutex=Mutex.new def buyTicket(num) @mutex.lock if @num>=num @num=@num-num puts "Sie haben erfolgreich #{num} Tickets gekauft" else puts "Entschuldigung, nicht genügend 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
Das Ausgaberesultat des obigen Codes ist:
Synchronisieren Sie den Thread Sie haben erfolgreich Tickets gekauft 15 tickets Sie haben erfolgreich Tickets gekauft 20 Tickets Sie haben erfolgreich Tickets gekauft 15 tickets Sie haben erfolgreich Tickets gekauft 20 Tickets Sie haben erfolgreich Tickets gekauft 15 tickets Sie haben erfolgreich Tickets gekauft 20 Tickets Sie haben erfolgreich Tickets gekauft 15 tickets Sie haben erfolgreich Tickets gekauft 20 Tickets Sie haben erfolgreich Tickets gekauft 15 tickets Sie haben erfolgreich Tickets gekauft 20 Tickets Sie haben erfolgreich Tickets gekauft 15 tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets Entschuldigung, nicht genügend Tickets
Neben dem Locken von Variablen mit lock kann auch mit try_lock gelockt werden, und Mutex.synchronize kann verwendet werden, um den Zugriff auf eine Variable zu synchronisieren.
Die Klasse Queue stellt eine Queue dar, die Threads unterstützt und den Zugriff auf das Ende der Queue synchronisiert. Differenten Threads kann dieselbe Klasse verwendet werden, aber es gibt keine Sorge darüber, ob die Daten in dieser Queue synchronisiert sind. Außerdem kann die Klasse SizedQueue die Länge der Queue begrenzen
Die Klasse SizedQueue hilft uns sehr leicht, Anwendungen zur Synchronisation von Threads zu entwickeln, da wir uns nicht um die Synchronisationsprobleme kümmern müssen, sobald wir diese Queue hinzufügen.
Klassisches Produzenten-Konsument-Problem:
#!/usr/bin/ruby require "thread" puts "SizedQuee Test" queue = Queue.new producer = Thread.new do 10.times do |i| sleep rand(i) # lassen Sie den Thread für eine Weile schlafen 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
Programmausgabe:
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
Threads können ihre eigenen Variablen haben, die bei der Erstellung eines Threads in den Thread geschrieben werden. Sie können im Bereich des Threads verwendet werden, aber sie können nicht von außen geteilt werden.
Aber was ist, wenn lokale Variablen eines Threads von anderen Threads oder dem Hauptthread benötigt werden? Ruby bietet die Möglichkeit, durch Namen Thread-variablen zu erstellen, ähnlich wie Threads als hash-artige Hash-Tabellen betrachtet werden. Daten können durch []= geschrieben und durch [] gelesen werden. Sehen wir uns den folgenden Code an:
#!/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}"
Die Ausgabe des obigen Codes lautet:
8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10
Das Hauptthread wartet auf die Abschluss eines Untethreads und gibt dann die Werte getrennt aus. 。
Die Priorität eines Threads ist der Hauptfaktor, der die Planung von Threads beeinflusst. Andere Faktoren umfassen die Dauer der CPU-Zeit, die von einem Thread in Anspruch genommen wird, und die Gruppierung der Schedulierung von Threads.
Man kann die Priorität eines Threads mit Thread.priority erhalten und die Priorität mit Thread.priority= anpassen.
Die Standardpriorität eines Threads beträgt 0. Threads mit höherer Priorität sollten schneller ausgeführt werden.
Ein Thread kann auf alle Daten in seinem Bereich zugreifen, aber was sollte man tun, wenn man auf Daten anderer Threads in einem bestimmten Thread zugreifen muss? Die Thread-Klasse bietet Methoden zur gegenseitigen Datenkommunikation zwischen Threads, man kann einen Thread einfach als Hash-Tabelle betrachten, in der Daten in jedem Thread mit []= geschrieben und mit [] gelesen werden können.
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"]}"}
Man kann sehen, dass wir durch die Verwendung von Threads als Hash-Tabelle und die Methoden [] und []= die Datenfreigabe zwischen Threads implementieren.
Mutex (Mutual Exclusion = Interlock) ist ein Mechanismus zur Vermeidung von parallelen Lesen und Schreiben von zwei Threads auf denselben gemeinsamen Ressource (z.B. globale Variablen) in der Mehrerebenen-Programmierung.
#!/usr/bin/ruby require 'thread' count1 =2 = 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 "count"1 : #{count1" puts "count"2 : #{count2" puts "difference : #{difference}"
Die folgenden Beispiel-Ausgabeergebnisse lauten:
count1 : 9712487 count2 : 12501239 difference : 0
#!/usr/bin/ruby require 'thread' mutex = Mutex.new count1 =2 = 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 "count"1 : #{count1" puts "count"2 : #{count2" puts "difference : #{difference}"
Die folgenden Beispiel-Ausgabeergebnisse lauten:
count1 : 1336406 count2 : 1336406 difference : 0
Mehrere Rechenzellen, beide warten darauf, dass der andere stoppt, um Systemressourcen zu erhalten, aber niemand tritt vorzeitig aus, dann wird dieser Zustand als Deadlock bezeichnet.
Zum Beispiel ein Prozess p1Besitz des Bildschirms, und gleichzeitig muss der Drucker verwendet werden, der von Prozess p2Besitz, p2Und wir müssen auch den Bildschirm verwenden, was zu einem Deadlock führt.
Wenn wir Mutex-Objekte verwenden, müssen wir auf Deadlocks achten.
#!/usr/bin/ruby require 'thread' mutex = Mutex.new cv = ConditionVariable.new a = Thread.new { mutex.synchronize { puts "A: Ich habe den kritischen Abschnitt, aber werde auf cv warten" cv.wait(mutex) puts "A: Ich habe wieder den kritischen Abschnitt! Ich regiere!" } } puts "(Später, zurück auf dem Ranch...)" b = Thread.new { mutex.synchronize { puts "B: Jetzt bin ich kritisch, aber habe cv beendet" cv.signal puts "B: Ich bin immer noch kritisch, beende" } } a.join b.join
Die obige Beispiel-Ausgabe ist wie folgt:
A: Ich habe einen kritischen Abschnitt, aber werde auf cv warten (Später, zurück auf dem Hof...) B: Jetzt bin ich kritisch, aber habe cv beendet B: Ich bin immer noch kritisch, beende A: Ich habe wieder einen kritischen Abschnitt! Ich regiere!
Die vollständigen Methoden der Thread-Klasse (Thread) sind wie folgt:
Nummer | Beschreibung der Methode |
---|---|
1 | Thread.abort_on_exception Wenn der Wert wahr ist, wird der gesamte Interpreter unterbrochen, wenn ein Thread aufgrund eines Ausnahmezustands beendet wird. Der Standardwert ist falsch, das bedeutet, in der Regel wird ein Thread, der eine Ausnahme erleidet und die Ausnahme nicht durch Thread#join oder ähnliches erkannt wird, ohne Warnung beendet. |
2 | Thread.abort_on_exception= Wenn gesetzt ist trueWenn ein Thread aufgrund eines Ausnahmezustands beendet wird, wird der gesamte Interpreter unterbrochen. Rückkehr zum neuen Zustand |
3 | Thread.critical Rückgabe eines Boolean-Werts. |
4 | Thread.critical= Wenn der Wert true ist, wird keine Thread-Wechsel durchgeführt. Wenn der aktuelle Thread angehalten (stop) oder durch ein Signal (signal) unterbrochen wird, wird der Wert automatisch auf false gesetzt. |
5 | Thread.current Rückkehr zum aktuellen laufenden Thread (aktuellster Thread). |
6 | Thread.exit Beendigung des Laufens des aktuellen Threads. Rückkehr zum aktuellen Thread. Wenn der aktuelle Thread der einzige Thread ist, wird exit(0) verwendet, um seinen Lauf zu beenden. |
7 | Thread.fork { block } Wie Thread.new erzeugt einen Thread. |
8 | Thread.kill( aThread ) Beendigung des Laufens eines Threads. |
9 | Thread.list Rückkehr zu einem Array von aktiven Threads im laufenden oder angehaltenen Zustand. |
10 | Thread.main Rückkehr zum Hauptthread. |
11 | Thread.new( [ arg ]* ) {| args | block } Erstellen eines Threads und Starten der Ausführung. Werte werden unverändert an den Block weitergegeben. Dies ermöglicht es, Werte gleichzeitig an lokale Variablen des Threads zu übergeben, wenn der Thread gestartet wird. |
12 | Thread.pass Das Laufrecht wird an andere Threads übergeben. Es ändert nicht den Zustand des laufenden Threads, sondern übergibt die Kontrolle an andere lauffähige Threads (ausdrückliche Thread-Scheduling). |
13 | Thread.start( [ args ])* ) {| args | block } Erstellen eines Threads und Starten der Ausführung. Werte werden unverändert an den Block weitergegeben. Dies ermöglicht es, Werte gleichzeitig an lokale Variablen des Threads zu übergeben, wenn der Thread gestartet wird. |
14 | Thread.stop Hält den aktuellen Thread an, bis ein anderer Thread den Thread durch den Aufruf der run-Methode wieder aktiviert. |
Nachfolgendes Beispiel ruft das Methodenbeispiel join auf:
#!/usr/bin/ruby thr = Thread.new do # Methodenbeispiel puts "In second thread" raise "Raise exception" end thr.join # Aufruf des Methodenbeispiels join
Hier ist eine vollständige Liste der Methodenbeispiele:
Nummer | Beschreibung der Methode |
---|---|
1 | thr[ name ] Auslesen der festgelegten Datenstruktur des Threads, die mit dem Namen name verbunden ist. name kann ein String oder ein Symbol sein. Wird keine Datenstruktur mit dem Namen name gefunden, wird nil zurückgegeben. |
2 | thr[ name ]= Einstellen des Wertes der festgelegten Datenstruktur des Threads, die mit dem Namen name verbunden ist. name kann ein String oder ein Symbol sein. Wenn nil eingestellt wird, wird die Datenstruktur im Thread gelöscht. |
3 | thr.abort_on_exception Rückgabe eines Boolean-Werts. |
4 | thr.abort_on_exception= Wenn der Wert true ist, wird der Interpreter unterbrochen, wenn ein Thread aufgrund eines Ausnahme abgebrochen wird. |
5 | thr.alive? Wird der Thread "aktiv", wird true zurückgegeben. |
6 | thr.exit Beenden der Ausführung des Threads. Rückgabe von self. |
7 | thr.join Anhalten des aktuellen Threads, bis der self-Thread die Ausführung beendet. Wenn self aufgrund eines Ausnahme abgebrochen wird, wird die gleiche Ausnahme im aktuellen Thread ausgelöst. |
8 | thr.key? Wird die festgelegte Datenstruktur des Threads, die mit dem Namen name verbunden ist, bereits definiert, wird true zurückgegeben. |
9 | thr.kill Ähnlich wie Thread.exit 。 |
10 | thr.priority Rückgabe der Priorität des Threads. Der Standardwert der Priorität ist 0. Der Wert ist umso höher, je größer die Priorität ist. |
11 | thr.priority= Einstellen der Priorität des Threads. Es kann auch eine negative Zahl eingestellt werden. |
12 | thr.raise( anException ) Erforderliche Auslösung eines Ausnahme im laufenden Thread. |
13 | thr.run Neustart des angehaltenen (stop) Threads. Im Gegensatz zu wakeup führt es sofort zu einem Thread-Wechsel. Wird diese Methode auf einen toten Prozess angewendet, wird eine ThreadError-Ausnahme ausgelöst. |
14 | thr.safe_level Gibt die Sicherheitsstufe von self zurück. Die safe_level des aktuellen Threads ist gleich $SAFE. |
15 | thr.status Verwendet die Strings "run", "sleep" oder "aborting", um den Status eines aktiven Threads anzuzeigen. Gibt false zurück, wenn ein Thread normal beendet wird. Gibt nil zurück, wenn der Thread aufgrund einer Ausnahme beendet wird. |
16 | thr.stop? Gibt true zurück, wenn der Thread im Zustand beendet(dead)oder angehalten(stop)ist. |
17 | thr.value Warte bis der self-Thread beendet ist (entspricht join), und gebe den Rückgabewert des Blocks des Threads zurück. Falls während der Ausführung des Threads eine Ausnahme auftritt, wird diese Ausnahme erneut ausgelöst. |
18 | thr.wakeup DenStatusdesangespannten(Stop)ThreadsindenausführbarenStatus(Run)umwandeln, falls diese Methode auf einen toten Thread angewendet wird, wird eine ThreadError-Ausnahme ausgelöst. |