Ruby multithreading


Release date:2023-11-03 Update date:2023-11-04 Editor:admin View counts:396

Label:

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:

  1. Thread synchronization through Mutex class

  2. The Queue class that supervises data handover implements thread synchronization

  3. 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

thr[ name ] fetch the inherent data corresponding to name in the thread.name can be a string or symbol. If there is no data corresponding to name, return nil.

2

thr[ name ] = sets the value of the inherent data corresponding to the name in the thread, and the name can be a string or symbol. If set to nil, the corresponding data in the thread will be deleted.

3

thr.abort_on_exception Returns a Boolean value.

4

thr.abort_on_exception= If the value is true, once a thread terminates due to an exception, the entire interpreter will be interrupted.

5

thr.alive? If the thread is “alive”, it returns true.

6

thr.exit Terminates the running of the thread. Return to self.

7

thr.join Suspends the current thread until the self thread terminates. If the self terminates because of an exception, the current thread throws the same exception.

8

thr.key? If the inherent data of the thread corresponding to name has been defined, return true

9

thr.kill is similar to Thread.exit.

10

thr.priority Returns the priority of the thread. The default value for priority is 0. The higher the value, the higher the priority.

11

thr.priority= Set the priority of threads. You can also set it to a negative number.

12

thr.raise( anException ) Forcibly throws an exception within the thread.

13

thr.run Restart the suspended (stop) thread. Unlike wakeup, it will switch threads immediately. If you use this method on a dead process, a ThreadError exception will be thrown.

14

thr.safe_level Returns the security level of self. The safe_level of thecurrent thread is the same as $SAFE.

15

thr.status use the string “run”, “sleep”, or “aborting” is used to represent the state of the living thread. If a thread terminates normally, it returns false. If it is terminated due to an exception, nil is returned.

16

thr.stop? If the thread is in a dead state or is stop, it returns true.

17

thr.value The return value of the thread’s block is returned after the self thread terminates running (equivalent to join). If an exception occurs while the thread is running, the exception is thrown again.

18

thr.wakeup Change the state of a suspended (stop) thread to an executable state (run). If this method is executed on a dead thread, a ThreadError exception will be thrown.

Powered by TorCMS (https://github.com/bukun/TorCMS).