Ruby - Multithreading

Multithreading:

Trong tin học khái niệm multithreading (đa luồng) chính là cách thực hiện của bất đồng bộ (asynchronous).

Theo wikipedia thì multithreading được định nghĩa như sau:

Multithreading is the ability of a central processing unit (CPU) or a single core in a multi-core processor to execute multiple processes or threads concurrently, appropriately supported by the operating system.

Nói một cách đơn giản gỉa sử ta có 2 công việc cần thực hiện là A và B, tương ứng với 2 đoạn code A và B . Thông thường A trên B thì A chạy xong( tức là A kết thúc) thì B mới có thể chạy. Tuy nhiên với lập trình đa luồng thì việc A có thể kết thúc hay chưa thì B vẫn có thể chạy.

Do đó lợi ích của nó chính là việc tạo cho ứng dụng khả năng linh hoạt cao.

Để viết các chương trình multi-threaded trong ruby, ruby đã cung cấp một lớp Thread.

Theo wikipedia thì Thread được định nghĩa như sau:

A thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system.

Ruby threads là một lightweight.

Vài điều cơ bản về Thread trước khi chuyển sang multithreading.

Thread.new{block} => tạo mới một thread, ngoài ra có thể dùng Thread.start, Thread.fork Thread.join => ngừng một thread đang chạy trong một khoảng thời gian(sleep(2): là 2s) để một thread mới chạy. Thread.current["name"] => gọi ra thread hiện thời Thread.status => trạng thái của thread, nó có 5 trạng thái là Runnable, Sleeping, Aborting, Terminatred normally, Terminated with exception. Thread.main => trả về Thread object Thread.priority => trả về độ ưu tiên của thread, ngoài ra có thể dùng Thread.priority= Thread Variables

Chương trình cơ bản sử dụng Multithreading:
def convert_string_to_uppercase str
    sleep(2)
    str.upcase
end

@str1 = "love ruby"
@str2 = "multithreading"
@str3 = "java"

threads = (1..3).map do |i|
  Thread.new(i) do |i|
    strs = instance_variable_get("@str#{i}")
    puts "string#{i} = #{convert_string_to_uppercase(strs)}"
  end
end
threads.each {|t| t.join}

Kết qủa chạy trương chình trên terminal:

......................................
2.2.3 :033 > threads.each {|t| t.join}
string1 = LOVE RUBY
string3 = JAVA
string2 = MULTITHREADING
 => [#<Thread:[email protected](irb):28 dead>, #<Thread:[email protected](irb):28 dead>, #<Thread:[email protected](irb):28 dead>]

Như trên ta thấy khi sử dụng Thread cho phép chạy đa luồng, ta không cần phải đợi @str1 được thực hiện song có kết qủa thì @str2 mới có kết qủa tương tự @str3 có kết qủa. chương trình được tiến hành đồng thời với Thread.

Race Conditions (Tương tranh):

Thread là tốt, song nếu ta viết nhiều code ứng dụng multithreaded thì gặp phải một vấn đề đó là sự tương tranh(race condition) giữa các thread khi truy cập cùng một resource.

Theo wikipedia race condition được định nghĩa như sau:

Race conditions arise in software when an application depends on the sequence or timing of processes or threads for it to operate properly. As with electronics, there are critical race conditions that result in invalid execution and bugs as well as non-critical race conditions that result in unanticipated behavior. Critical race conditions often happen when the processes or threads depend on some shared state.

Ví dụ:

class Item
  class << self; attr_accessor :price end
  @price = 0
end

threads = (1..10).map do |i|
  Thread.new(i) do |i|
    item_price = Item.price # Reading value
    sleep(rand(0..2))
    item_price += 10        # Updating value
    sleep(rand(0..2))
    Item.price = item_price # Writing value
  end
end

threads.each {|t| t.join}

puts "Item.price = #{Item.price}"

=> Chạy trên terminal ta thấy được chương trình trên cho ra kết qủa không chính xác như mong đợi. lẽ ra kết qủa chính xác phải là 100. Nhưng mỗi lần thực hiện chương trình trên kết qủa cho ra rất khác nhau có thể là 30, 40 or 70....nguyên nhân chính là do sự tương tranh (race conditions).

Để giải quyết vấn đề race conditions trong ruby ta dùng Mutual Exclusion

Mutual Exclusion (Loại trừ tương hỗ):

Theo wikipedia Mutual Exclusion được định nghĩa như sau:

In computer science, mutual exclusion refers to the requirement of ensuring that no two concurrent processes[a] are in their critical section at the same time; it is a basic requirement in concurrency control, to prevent race conditions.

Với ví dụ ở trên giờ ta sẽ giải quyết nó bằng Mutual Exclusion như sau:

class Item
  class << self; attr_accessor :price end
  @price = 0
end

mutex = Mutex.new

threads = (1..10).map do |i|
  Thread.new(i) do |i|
    mutex.synchronize do
      item_price = Item.price # Reading value
      sleep(rand(0..2))
      item_price += 10        # Updating value
      sleep(rand(0..2))
      Item.price = item_price # Writing value
    end
  end
end

threads.each {|t| t.join}

puts "Item.price = #{Item.price}"

Kết qủa chạy trên terminal:

...............................................
2.2.3 :022 >   puts "Item.price = #{Item.price}"
Item.price = 100
2.2.3 :023 >
 => nil

Sở dĩ race conditions được giải quyết là vì ta có thêm vào trong code một đoạn là mutex mutex.synchronize => nghĩa là một và chỉ một thread có thể truy cập một block wrapped trong mutex.synchronize ở bất kỳ thời điểm nào. các thread khác phải đợi cho đến khi thread hiện thời được xử lý xong.

Còn một vấn đề nữa đó là nếu gỉa sử thread hiện thời vì một lý do gì đó không thể kết thúc thì các thread khác cứ phải đợi và không có một thread nào được xử lý tiếp theo => đó chính là Deadlock.

Deadlock:

Theo wikipedia Deadlock được định nghĩa như sau:

In concurrent programming, a deadlock is a situation in which two or more competing actions are each waiting for the other to finish, and thus neither ever does.

Có thể tìm hiểu rõ hơn về Deadlock ở link sau: http://www.tutorialspoint.com/ruby/ruby_multithreading.htm