Ruby Multithreading
Each program running on the system is a process. Each process contains one or more threads.
A thread is a single sequential program control flow, simultaneously run multiple threads in a single program do different things, called multi-threading.
Ruby, we can be created by multiple threads Thread class, Ruby threads are a lightweight, it can be an efficient way to implement parallel code.
Creating a Ruby thread
To start a new thread, you can just call Thread.new:
# 线程 #1 代码部分 Thread.new { # 线程 #2 执行代码 } # 线程 #1 执行代码
Examples
The following example shows how to use multithreading in 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 above code is executed as a result of:
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, create a thread can use Thread.new, you can also use the same syntax Thread.start or Thread.fork three ways to create a thread.
2, create a thread without starting, the thread will be executed automatically.
3, Thread class defines methods to manipulate thread. Thread of execution Thread.new code blocks.
4, the thread block is the value of the last statement in the thread, the thread can be invoked by a method, if the thread is finished, it returns the value of the thread, or does not return a value until the thread is finished.
5, Thread.current method returns an object for the current thread representation. Thread.main method returns the main thread.
6, the method is performed by Thread.Join threads, this method will suspend the main thread until the current thread is finished.
Thread State
Thread has five states:
Thread State | return value |
---|---|
Executable | run |
Sleeping | Sleeping |
drop out | aborting |
Normal termination | false |
Abnormal termination occurs | nil |
Threads and aberrant
When a thread exception occurs, and no rescue was captured, the thread would normally be terminated without warning. However, if other threads Thread # join because the relationship has been waiting for this thread, then the waiting threads will also be raised the same exception.
begin t = Thread.new do Thread.pass # 主线程确实在等join raise "unhandled exception" end t.join rescue p $! # => "unhandled exception" end
Use the following three methods, you can get the interpreter to interrupt the operation when a thread terminates due to an exception.
- Startup script specifies-d option, and debugging mode operation.
- With
Thread.abort_on_exception
set the flag. - Use
Thread#abort_on_exception
specified thread set flag.
When using one of three methods described above, the entire interpreter will be interrupted.
t = Thread.new { /en. } t.abort_on_exception = true
Thread Synchronization
In Ruby, provides three synchronized manner, namely:
1. Mutex class implements Thread Synchronization
2. Regulatory data transfer Queue class implement thread synchronization
3. Use ConditionVariable synchronization control
By Mutex class implements Thread Synchronization
By Mutex class implements the thread synchronization control, if you also need a program variable clock in multiple threads, you can use the lock to lock the variable part. 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,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
The above code is executed as a result of:
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 using lock locked variable, you can also use try_lock locked variable, you can also use Mutex.synchronize synchronize access to a particular variable.
Regulatory data transfer of the Queue class implements Thread Synchronization
Queue class that represents a thread support queue, the queue can be synchronized to the end of the visit. Different threads can use a unified class, but do not worry about whether the data can be synchronized in this queue, in addition to limit the use of SizedQueue class queue length
SizedQueue class can be very convenient to help us develop threaded applications to synchronize, synchronization problems should be added to the long queue, you do not care about threads.
Classic producers and consumers:
#!/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
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
Thread Variables
Thread can have its private variable, a thread private variable writing thread when the thread is created. It can be used within the scope of the thread, but the thread can not be shared externally.
But sometimes, the thread local variables do not need another thread or the main thread to access how to do? ruby allows them to create a thread is provided by the variable name, the thread is seen as similar style hash hash table. By [] = written by [] read data. We look at the following code:
#!/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 above code is run output is:
8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10
The main thread waits for the child thread execution is completed, and then outputs each value. .
Thread priority
Thread's priority is the main factor affecting the thread scheduling. Other factors include the length of time to perform CPU-thread packet scheduling and so on.
You can use Thread.priority obtained a thread's priority and use Thread.priority = method to adjust the thread priority.
A thread's priority defaults to 0. Faster execution of higher priority.
A Thread can access all data within the scope of their own, but if there are other threads need to access data in a thread should be how to do it? Thread class provides thread data access each other, you can simply put a thread as a Hash table, can be used in any thread [] = Write data, use [] read 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"]}" }
We can see, the thread as a Hash table, use [] and [] = method, we achieved data sharing between threads.
Thread mutex
Mutex (Mutal Exclusion = mutex) is a method for multi-threaded programming, a mechanism to prevent two threads simultaneously for the same public resources (such as global variables) read and write.
Examples of non-use 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}"
Run the above example output is:
count1 : 9712487 count2 : 12501239 difference : 0
Example of 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}"
Run the above example output is:
count1 : 1336406 count2 : 1336406 difference : 0
Deadlock
More than two operation units, both sides are waiting for the other to stop running, to get the system resources, but not a party early exit, this situation is called deadlock.
For example, a process p1 takes up display, while you must use the printer, and the printer is occupied by the process p2, p2 must also use the monitor, thus forming a deadlock.
When we use Mutex object needs attention thread deadlock.
Examples
#!/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/en.)" 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 above example output is:
A: I have critical section, but will wait for cv (Later, back at the ranch/en.) 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
Complete Thread (thread) class method as follows:
No. | Method Description |
---|---|
1 | Thread.abort_on_exception If it is true, once a thread terminates due to an exception, the entire interpreter will be interrupted. The default value is false, that is, under normal circumstances, if a thread exception occurs and the exception is not Thread # join and other detected, the thread will be terminated without warning. |
2 | Thread.abort_on_exception = If set totrue,once a thread terminates due to an exception, the entire interpreter will be interrupted. Returns new state |
3 | Thread.critical Returns a Boolean value. |
4 | Thread.critical = When the value is true, the thread will not be switched. If the current thread to hang (stop) or signal (signal) intervention, its value will be automatically changed to false. |
5 | Thread.current Returns the current running thread (the current thread). |
6 | Thread.exit It terminates the current thread. Returns the current thread. If the current thread is the only thread, using the exit (0) to terminate its operation. |
7 | Thread.fork {block} Like with Thread.new generate threads. |
8 | Thread.kill (aThread) Terminate the thread running. |
9 | Thread.list Returns an array of live thread is running or suspended state. |
10 | Thread.main Return to the main thread. |
11 | Thread.new ([arg] *) { | args | block} Generate thread and begin execution. The number will be passed intact to the block. This can start a thread at the same time, the value will be passed to the local variables inherent in the thread. |
12 | Thread.pass The right to run other threads. It does not change the state of the running threads, but will hand over control of other threads can run (Explicit thread scheduling). |
13 | Thread.start ([args] *) { | args | block} Generate thread and begin execution. The number will be passed intact to the block. This can start a thread at the same time, the value will be passed to the local variables inherent in the thread. |
14 | Thread.stop The current thread is suspended until the other threads run method again wake up the thread. |
Thread instance method
The following example calls the thread instance method join:
#!/usr/bin/ruby thr = Thread.new do # 实例化 puts "In second thread" raise "Raise exception" end thr.join # 调用实例化方法 join
Here is a complete list of examples of the method:
No. | Method Description |
---|---|
1 | thr [name] Remove the thread of the name corresponding to the inherent data. name can be a string or symbol. If the name does not correspond to the data, it returns nil. |
2 | thr [name] = Set the thread name value in the corresponding characteristic data, name can be a string or symbol. If set to nil, removes the corresponding data in this thread. |
3 | thr.abort_on_exception Returns a Boolean value. |
4 | thr.abort_on_exception = If its value is true, then once a thread terminates due to an exception, the entire interpreter will be interrupted. |
5 | thr.alive? If the thread is "live", it returns true. |
6 | thr.exit Terminate the thread running. Returns self. |
7 | thr.join Suspends the current thread until the self run until the thread terminates. If the self due to abnormal termination, the current thread will trigger the same exception. |
8 | thr.key? If the name corresponding to the inherent data has been defined threads, then returns true |
9 | thr.kill SimilarThread.exit. |
10 | thr.priority Returns the thread's priority. The default priority is 0. The larger the value, the higher the priority. |
11 | thr.priority = Setting the thread priority. You can also set it to negative. |
12 | thr.raise (anException) Within this thread forcibly thrown. |
13 | thr.run Restart Pending (stop) thread. The difference is that with the wakeup will conduct thread switching immediately. If using this method to process the dead will be raised ThreadError exception. |
14 | thr.safe_level Returns self security level. Safe_level the current thread $ SAFE same. |
15 | thr.status Using the string "run", "sleep" or "aborting" to indicate the status of the live thread if a thread is terminated normally, then it returns false. Ruoyin abnormal termination, then it returns nil. |
16 | thr.stop? If the thread is terminated state (dead) or suspend (stop), the return true. |
17 | thr.value Wait until the self thread terminates (equivalent to join), return the value of the block of the thread if the running threads occur during abnormal, the exception will be raised again. |
18 | thr.wakeup The state is suspended (stop) of thread to the ready state (run), if the method is performed on the dead thread will raise ThreadError exception. |