English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Each program running on the system is a process. Each process contains one or more threads.
A thread is a single sequential control flow in a program. Running multiple threads simultaneously in a single program to complete different tasks is called multi-threading.
In Ruby, we can create multi-threading through the Thread class. Ruby's threads are lightweight and can be used to implement parallel code efficiently.
To start a new thread, simply call Thread.new:
# Thread #1 Code section Thread.new { # Thread #2 Execute code } # Thread #1 Execute code
The following example demonstrates how to use multi-threading in a Ruby program:
#!/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}"
The execution result of the above code is:
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
1The creation of a thread can use Thread.new, and it can also use Thread.start or Thread.fork to create a thread with the same syntax.
2After creating a thread, there is no need to start it, the thread will execute automatically.
3The Thread class defines some methods to control threads. The thread executes the code block in Thread.new.
4The last statement in the thread code block is the value of the thread, which can be called through the thread's methods. If the thread has completed execution, then the thread value is returned, otherwise no value is returned until the thread is completed.
5By using the Thread.current method, it returns an object representing the current thread. The Thread.main method returns the main thread.
6By using the Thread.Join method to execute the thread, this method will suspend the main thread until the current thread is completed.
There are5There are
thread status | return value |
---|---|
executable | run |
sleep | Sleeping |
exit | aborting |
Normal termination | false |
Exception termination | nil |
When a thread occurs an exception and is not caught by rescue, the thread is usually terminated without warning. However, if other threads are waiting for this thread due to Thread#join, then the waiting threads will also be triggered by the same exception.
begin t = Thread.new do Thread.pass # The main thread is indeed waiting for join raise "unhandled exception" end t.join rescue p $! # => "unhandled exception" end
Use the following3This method can cause the interpreter to interrupt its execution when a thread terminates due to an exception.
Specify when starting the script.-dOption, and run in debug mode.
Using Thread.abort_on_exception to set the flag.
Use Thread#abort_on_exception to set the flag for the specified thread.
When using the above3After using this method, the entire interpreter will be interrupted.
t = Thread.new { ... } t.abort_on_exception = true
In Ruby, there are three ways to implement synchronization, namely:
1Through the Mutex class to implement thread synchronization
2. The Queue class that regulates data transfer implements thread synchronization
3. Use ConditionVariable to implement synchronization control
Thread synchronization control is implemented through the Mutex class. If a program variable is needed simultaneously by multiple threads, part of this variable can be locked using lock. The code is as follows:
#!/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, not 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
The execution result of the above code is:
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, not enough tickets Sorry, not enough tickets Sorry, not enough tickets Sorry, not enough tickets Sorry, not enough tickets Sorry, not enough tickets Sorry, not enough tickets Sorry, not enough tickets Sorry, not enough tickets
In addition to using lock to lock variables, try_lock can also be used to lock variables, and Mutex.synchronize can be used to synchronize access to a variable.
The Queue class represents a thread-safe queue that can synchronize access to the end of the queue. Different threads can use the same instance of the class, but there is no need to worry about the synchronization of the data in the queue. Additionally, using the SizedQueue class can limit the length of the queue.
The SizedQueue class can greatly facilitate the development of thread-synchronized applications, as it is not necessary to worry about thread synchronization as long as items are added to this queue.
The classic producer-consumer problem:
#!/usr/bin/ruby require "thread" puts "SizedQueue Test" queue = Queue.new producer = Thread.new do 10.times do |i| sleep rand(i) # Let the thread sleep for a while 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
Program output:
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 can have their private variables, which are written to the thread when the thread is created. They can be used within the scope of the thread but cannot be shared outside the thread.
But what if the local variables of a thread need to be accessed by other threads or the main thread? Ruby provides a way to create thread variables by name, similar to treating threads as hash-style hash tables. Data can be written using []= and read using [].
#!/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}"
The output result of the above code is:
8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10
The main thread waits for the subthread to complete and then outputs each value separately. .
The priority of a thread is a major factor affecting thread scheduling. Other factors include the length of time a thread occupies the CPU, thread grouping scheduling, etc.
You can get the priority of a thread using the Thread.priority method and adjust the priority using Thread.priority=.
The default priority of a thread is 0. Threads with higher priority should execute faster.
A thread can access all the data within its scope, but what should you do if you need to access data from another thread within a certain thread? The Thread class provides methods for threads to access each other's data, and you can simply treat a thread as a hash table, where you can write data using []= within any thread and read data using [].
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"]}"}
As can be seen, by treating threads as a hash table and using the [] and []= methods, we have achieved data sharing between threads.
Mutex (Mutual Exclusion = mutual exclusion lock) is a mechanism used in multithreaded programming to prevent two threads from reading and writing the same public resource (such as global variables) at the same time.
#!/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}"
The following is the output result of the example run:
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}"
The following is the output result of the example run:
count1 : 1336406 count2 : 1336406 difference : 0
Two or more computational units are waiting for each other to stop running to obtain system resources, but no one exits in advance, this situation is called deadlock.
For example, a process p1Occupied the display and must also use the printer, while the printer is occupied by process p2Occupation, p2And it must use the display, thus forming a deadlock.
When using Mutex objects, we need to be aware of thread deadlock.
#!/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
The output result of the above example is as follows:
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!
The complete Thread (thread) class methods are as follows:
Serial Number | Method Description |
---|---|
1 | Thread.abort_on_exception If its value is true, once a thread terminates due to an exception, the entire interpreter will be interrupted. Its default value is false, which means that in normal circumstances, if a thread occurs an exception and the exception is not detected by Thread#join or other detection, the thread will be terminated without warning. |
2 | Thread.abort_on_exception= If set to trueOnce a thread terminates due to an exception, the entire interpreter will be interrupted. Return the new status |
3 | Thread.critical Return a boolean value. |
4 | Thread.critical= When its value is true, thread switching will not occur. If the current thread is suspended (stop) or a signal (signal) intervenes, its value will automatically become false. |
5 | Thread.current Return the currently running thread (current thread). |
6 | Thread.exit Terminate the execution of the current thread and return the current thread. If the current thread is the only one, it will use exit(0) to terminate its execution. |
7 | Thread.fork { block } Generate a thread like Thread.new. |
8 | Thread.kill( aThread ) Terminate the execution of the thread. |
9 | Thread.list Return an array of live threads that are in a running or suspended state. |
10 | Thread.main Return to the main thread. |
11 | Thread.new( [ arg ]* ) {| args | block } Generates a thread and starts executing. Values will be passed to the block unchanged. This allows values to be passed to the local variables inherent to the thread when starting the thread. |
12 | Thread.pass Pass the execution right to another thread. It will not change the state of the running thread, but will hand over control to another runnable thread (explicit thread scheduling). |
13 | Thread.start( [ args ])* ) {| args | block } Generates a thread and starts executing. Values will be passed to the block unchanged. This allows values to be passed to the local variables inherent to the thread when starting the thread. |
14 | Thread.stop Suspends the current thread until the thread using the run method wakes it up again. |
The following example calls the thread example method join:
#!/usr/bin/ruby thr = Thread.new do # Example puts "In second thread" raise "Raise exception" end thr.join # Call the example method join
The following is a complete list of example methods:
Serial Number | Method Description |
---|---|
1 | thr[ name ] Retrieve the inherent data corresponding to the name within the thread. Name can be a string or symbol. If there is no data corresponding to the name, return nil. |
2 | thr[ name ]= Set the value of the inherent data corresponding to the name within the thread, where name can be a string or symbol. If set to nil, the corresponding data within the thread will be deleted. |
3 | thr.abort_on_exception Return a boolean value. |
4 | thr.abort_on_exception= If the value is true, the entire interpreter will be interrupted once a thread terminates due to an exception. |
5 | thr.alive? If the thread is 'alive', return true. |
6 | thr.exit Terminate the thread's execution. Return self. |
7 | thr.join Suspend the current thread until the self thread stops running. If self terminates due to an exception, the same exception will be raised in the current thread. |
8 | thr.key? If the inherent data of the thread corresponding to the name has been defined, return true |
9 | thr.kill Similar to Thread.exit . |
10 | thr.priority Return the thread's priority. The default value of priority is 0. The higher the value, the higher the priority. |
11 | thr.priority= Set the thread's priority. It can also be set to a negative number. |
12 | thr.raise( anException ) Forcefully raise an exception within this thread. |
13 | thr.run Restart the suspended (stop) thread. Unlike wakeup, it will immediately switch to the thread. If this method is used on a dead process, it will raise a ThreadError exception. |
14 | thr.safe_level Returns the safety level of self. The safe_level of the current thread is the same as $SAFE. |
15 | thr.status Uses the strings "run", "sleep", or "aborting" to represent the state of a live thread. If a thread terminates normally, it returns false. If it terminates due to an exception, it returns nil. |
16 | thr.stop? Returns true if the thread is in a terminated (dead) or suspended (stop) state. |
17 | thr.value Waits until the self thread terminates (equivalent to join) and returns the return value of the block of the thread. If an exception occurs during the execution of the thread, the exception will be raised again. |
18 | thr.wakeup Changes the state of a suspended (stop) thread to an executable (run) state. If this method is called on a dead thread, a ThreadError exception will be raised. |