Ruby multithreading
Every program running on the system is a process. Each process contains one or more threads.
Thread is a single sequence control flow in a program, which runs multiple threads to complete different tasks at the same time in a single program, which is called multi-thread.
In Ruby, we can pass through Thread
class to create multithreading, Ruby threads are lightweight and can implement parallel code in an efficientway.
Create a Ruby thread
To start a new thread, simply call the Thread.new
:
#Thread # 1 Code Section Thread. new
{# Thread # 2 Executing Code} # Thread # 1 Executing Code
Example
The following example shows how to use multithreading in a Ruby program:
Example
#!/usr/bin/rubydeffunc1i=0whilei<=2puts"func1 at:
#{Time.now}"sleep(2)i=i+1endenddeffunc2j=0whilej<=2puts"func2 at:
#{Time.now}"sleep(1)j=j+1endendputs"Started At
#{Time.now}"t1=Thread.new{func1()}t2=Thread.new{func2()}t1.joint2.joinputs"End
at #{Time.now}"
The result of the above code execution 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
Thread life cycle
1.Thread creation can be done using the Thread.new
can also be used inthe same syntax Thread.start
or Thread.fork
, these three methods create threads.
2.There is no need to start the thread after it is created, and the thread will execute automatically.
3.The Thread class defines some methods to manipulate threads. Thread execution Thread.new
code block .
4.The last statement in the thread code block is the value of the thread, which can be called through the method of the thread. If the thread is finished, the thread value is returned, otherwise the value is not returned until the thread is finished.
5.The Thread.current
method returns the object that represents the current thread. Thread.main
method returns the main thread.
6.Through Thread.Join
method to execute the thread, which suspends themain thread until the current thread finishes execution.
Thread state
Threads have five states:
Thread state |
Return value |
---|---|
Executable |
Run |
sleep |
Sleeping |
Quit |
Aborting |
Normal termination |
False |
Abnormal termination occurred |
Nil |
Threads and exceptions
When an exception occurs in a thread and is not subject to rescue
when captured, the thread is usually terminated without warning. However, if there are other threads because Thread#join
if the relationship has been waiting for the thread, the waiting thread will be thrown the same exception.
begint=Thread.newdoThread.pass#The main thread is indeed waitingjoinraise"unhandled
exception"endt.joinrescuep$!#=> "unhandled exception"end
Using the following three methods, you can have the interpreter break when athread terminates due to an exception.
Specify when you start the script
-d
option and run when debugging the mode.Use
Thread.abort_on_exception
Set the flag.Use
Thread#abort_on_exception
Sets the flag for the specified thread.
When using one of the above three methods, the entire interpreter will be interrupted.
t=Thread.new{ ... }t.abort_on_exception=true
Thread synchronization control
In Ruby, there are three ways to achieve synchronization, which are:
Thread synchronization through Mutex class
The Queue class that supervises data handover implements thread synchronization
Use
ConditionVariable
to realize synchronous control
Thread synchronization through Mutex class
Thread synchronization control is implemented through the Mutex class, and if you need a program variable for multiple thread clocks at the same time, you can use lock to lock part of this variable. The code is as follows:
Example
#!/usr/bin/rubyrequire"thread"puts"Synchronize
Thread"@num=200@mutex=Mutex.newdefbuyTicket(num)@mutex.lockif@num>=num@num=@num-numputs"you
have successfully bought #{num} tickets"elseputs"sorry,no enough
tickets"end@mutex.unlockendticket1=Thread.new10do10.timesdo\|value\|ticketNum
=15buyTicket(ticketNum)sleep0.01endendticket2=Thread.
new10do10.timesdo\|value\|ticketNum=20buyTicket(ticketNum)sleep0.01endendsleep1ticket1.jointicket2.join
The result of the above code execution 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,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
In addition to locking variables using lock, you can also use the try_lock
lock variables, and you can also use the Mutex.synchronize
synchronize access to a variable.
The Queue class that supervises data handover implements thread synchronization
The Queue class represents a queue that supports threads and can access the end of the queue synchronously. Different threads can use a unified pair class, but do not worry about whether the data in this queue can be synchronized, and use the SizedQueue
class can limit the length of the queue
SizedQueue
class can easily help us develop thread synchronization applications, so as long as you join this queue, you don’t have to worry about thread synchronization.
The classic producer-consumer problem:
Example
#!/usr/bin/rubyrequire"thread"puts"SizedQuee
Test"queue=Queue.newproducer=Thread.newdo10.timesdo\|i\|sleeprand
(i)#Let the thread sleep for a period of timequeue<<iputs"#{i}
produced"endendconsumer=Thread.newdo10.timesdo\|i\|value=queue.popsleeprand(i/2)
puts "consumed #{value}" end end consumer.join
Output of the program:
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
Thread variable
A thread can have its own private variable, and the thread’s private variable is written to the thread when it is created. It can be used within the scope of the thread, but cannot be shared outside the thread.
But sometimes, what if a thread’s local variables need to be accessed by another thread or the main thread? Ruby provides the ability to create thread variables by name, similar to treating threads as hash-style hash tables. Pass through []=
write and pass through []
read the data. Let’s look at the following code:
Example
#!/usr/bin/rubycount=0arr=[]10.timesdo\|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 of the above code is as follows:
8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10
The main thread waits for the child thread to finish execution, and then outputs each value separately. .
Thread priority
The priority of the thread is the main factor affecting the scheduling of the thread. Other factors include the amount of execution time consumed by CPU, thread grouping scheduling, and so on.
Can be used Thread.priority
method to get the priority and use of the thread Thread.priority=
method to adjust the priority of the thread.
The priority of the thread defaults to 0. Those with higher priority executefaster.
An Thread can access all the data in its scope, but what if you need to access the data of other threads within one thread? The Thread class provides a way for threads to access each other’s data. You can simply use a thread as a Hash table in any thread. []=
write data, use []
to read the data.
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 you can see, using the thread as a Hash table, use the []
and []=
method, we implement data sharing between threads.
Thread mutual exclusion
Mutex (Mutal Exclusion = mutex) is a mechanism used in multithreaded programming to prevent two threads from reading and writing to the same common resource, such as global variables.
Instances that do not use Mutax
Example
#!/usr/bin/rubyrequire'thread'count1=count2=0difference=0counter=
Thread.newdoloopdocount1+=1count2+=1endendspy=Thread.newdoloopdodifference+
=(count1-count2).absendendsleep1puts"count1
: #{count1}"puts"count2 : #{count2}"puts"difference : #{difference}"
The output of the above instance is as follows:
count1 : 9712487
count2 : 12501239
difference : 0
An instance of using mutex
Example
#!/usr/bin/rubyrequire'thread'mutex=Mutex.newcount1=count2=0difference=0counter=Thread.newdoloopdomutex.synchronizedocount1+=1count2+=1endendendspy=Thread.newdoloopdomutex.synchronizedodifference+=(count1-count2).absendendendsleep1mutex.lockputs"count1
: #{count1}"puts"count2 : #{count2}"puts"difference : #{difference}"
The output of the above instance is as follows:
count1 : 1336406
count2 : 1336406
difference : 0
Deadlock
When there are more than two computing units, both sides are waiting for theother to stop running in order to obtain system resources, but when no one exits ahead of time, this situation is called deadlock.
For example, a process p1 takes up the monitor and must use the printer at the same time, while the printer is occupied by process p2, and p2 must use the monitor, thus forming a deadlock.
When we are using Mutex
object needs to be aware of thread deadlocks.
Example
#!/usr/bin/rubyrequire'thread'mutex=Mutex.newcv=ConditionVariable.newa=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.signalputs"B: I am still critical, finishing up"}
}a.joinb.join
The output result of the above example is:
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!
Thread class method
The complete Thread class method is as follows:
Serial number |
Method description |
---|---|
1 |
If the value of Thread.abort_on_exception is true, once a thread terminates due to an exception, the entire interpreter will be interrupted. Its defaultvalue is false, that is, under normal circumstances, if an exception occursto a thread and the exception is not detected by Thread#join, etc., the thread will be terminated without warning. |
2 |
If Thread.abort_on_exception= is set to true, once a thread terminates due to an exception, the entire interpreter will be interrupted. Return to the new status |
3 |
Thread.critical returns a Boolean value. |
4 |
Thread.critical= will not switch threads when its value is true. If the current thread is suspended (stop) or interfered with a signal, itsvalue automatically changes to false. |
5 |
Thread.current returns the currently running thread. |
6 |
Thread.exit terminates the running of the current thread. Returns the current thread. If the current thread is the only thread, exit (0) will be used to terminate its run. |
7 |
Thread.fork {block} generates threads like Thread.new. |
8 |
Thread.kill (aThread) terminates the running of the thread. |
9 |
Thread.list returns an array of live threads in the running or suspended state. |
10 |
Thread.main returns the main thread. |
11 |
Thread.new( [ arg ]* ) {| args | block }Generate a thread and begin execution. The number will be passed to the block intact This allows the value to be passed to the local variables inherent in the thread while starting it. |
12 |
Thread.pass gives the right to run to other threads. It does not change the state of the running thread, but gives control to other runnable threads (explicit thread scheduling). |
13 |
Thread.start( [ args ]* ) {| args | block }Generate a thread and begin execution. The number will be passed to the block intact This allows the value to be passed to the local variables inherent in the thread while starting it. |
14 |
Thread.stop suspends the current thread until another thread wakes it up again using the run method. |
Thread instantiation method
The following example calls the thread instantiation method join
:
Example
#!/usr/bin/rubythr=Thread.newdo#instantiate puts"In second thread"raise"Raise
exception"endthr.join#Call Instantiation Method join
The following is a complete list of instantiation methods:
Serial number |
Method description |
---|---|
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|