Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby 스레드 사용 방법:이해하기 쉬운 튜토리얼

Ruby의 스레드란 무엇입니까?

스레드는 Ruby 프로그램이 동시에 여러 작업을 수행하도록 합니다.

예: :

  • 여러 파일 읽기
  • 여러 웹 요청 처리
  • 여러 API 연결 만들기

스레드를 사용하면 작업을 더 빠르게 완료할 수 있는 다중 스레드 Ruby 프로그램을 갖게 됩니다.

하지만 하나의 경고...

Ruby 애플리케이션을 실행하는 기본 방법인 MRI(Matz의 Ruby Interpreter)에서는 i/o 바운드 애플리케이션을 실행할 때만 스레드의 이점을 누릴 수 있습니다. .

이 제한은 GIL(Global Interpreter Lock) 때문에 존재합니다. .

JRuby 또는 Rubinius와 같은 대체 Ruby 인터프리터는 멀티스레딩을 최대한 활용합니다.

Ruby 스레드 사용 방법:이해하기 쉬운 튜토리얼

스레드란 무엇인가요?

스레드는 작업자 또는 실행 단위입니다.

모든 프로세스에는 하나 이상의 스레드가 있으며 요청 시 추가로 생성할 수 있습니다.

코드 예제를 보고 싶어한다는 것을 알고 있습니다.

하지만 먼저 CPU 바운드 애플리케이션과 I/O 바운드 애플리케이션의 차이점에 대해 이야기해야 합니다.

I/O 바인딩된 애플리케이션

i/o 바인딩된 앱 외부 리소스를 기다려야 하는 것입니다.

  • API 요청
  • 데이터베이스(쿼리 결과)
  • 디스크 읽기

스레드는 리소스를 사용할 수 있을 때까지 기다리는 동안 중지를 결정할 수 있습니다. 즉, 다른 스레드가 실행되어 해당 작업을 수행하고 대기 시간을 낭비하지 않을 수 있습니다.

i/o 바운드 앱의 한 예 웹 크롤러입니다.

모든 요청에 ​​대해 크롤러는 서버가 응답할 때까지 기다려야 하며 기다리는 동안에는 아무 것도 할 수 없습니다.

하지만 쓰레드를 사용한다면...

한 번에 4개의 요청을 수행하고 응답이 돌아올 때 처리할 수 있으므로 페이지를 더 빨리 가져올 수 있습니다.

이제 코드 예제를 작성할 차례입니다.

루비 스레드 생성

Thread.new를 호출하여 새로운 Ruby 스레드를 생성할 수 있습니다. .

이 스레드를 실행하는 데 필요한 코드가 포함된 블록을 전달해야 합니다.

Thread.new { puts "hello from thread" }

꽤 쉽죠?

그러나.

다음 코드가 있는 경우 스레드에서 출력이 없음을 알 수 있습니다.

t = Thread.new { puts 10**10 }
puts "hello"

문제는 Ruby가 스레드가 끝날 때까지 기다리지 않는다는 것입니다.

join을 호출해야 합니다. 위의 코드를 수정하려면 스레드에서 메서드를 사용하세요.

t = Thread.new { puts 10**10 }
puts "hello"
t.join

여러 스레드를 만들고 싶다면 배열 안에 넣고 join을 호출하면 됩니다. 모든 스레드에서.

:

threads = []

10.times {
  threads << Thread.new { puts 1 }
}

threads.each(&:join)

Ruby 스레드를 탐색하는 동안 유용한 문서를 찾을 수 있습니다.

https://ruby-doc.org/core-2.5.0/Thread.html

스레드 및 예외

스레드 내부에서 예외가 발생하면 프로그램을 중지하거나 어떤 종류의 오류 메시지도 표시하지 않고 조용히 종료됩니다.

다음은 예입니다:

Thread.new { raise 'hell' }

디버깅 목적으로 나쁜 일이 발생하면 프로그램이 중지되기를 원할 수 있습니다. 이를 위해 Thread에 다음 플래그를 설정할 수 있습니다. 참으로:

Thread.abort_on_exception = true

스레드를 생성하기 전에 이 플래그를 설정해야 합니다. 🙂

스레드 풀

처리할 항목이 수백 개 있다고 가정해 보겠습니다. 각 항목에 대해 스레드를 시작하면 시스템 리소스가 파괴됩니다.

다음과 같이 보일 것입니다:

pages_to_crawl = %w( index about contact ... )

pages_to_crawl.each do |page|
  Thread.new { puts page }
end

이렇게 하면 서버에 대해 수백 개의 연결을 시작하게 되므로 좋은 생각이 아닐 것입니다.

한 가지 해결책은 스레드 풀을 사용하는 것입니다.

스레드 풀을 사용하면 주어진 시간에 활성 스레드 수를 제어할 수 있습니다.

자신의 수영장을 만들 수는 있지만 권장하지는 않습니다. 다음 예에서는 셀룰로이드 보석을 사용하여 이 작업을 수행합니다.

<블록 인용>

참고:셀룰로이드는 이제 유지 관리되지 않지만 작업자 풀의 일반적인 개념은 여전히 ​​적용됩니다.

require 'celluloid'

class Worker
  include Celluloid

  def process_page(url)
    puts url
  end
end

pages_to_crawl = %w( index about contact products ... )
worker_pool    = Worker.pool(size: 5)

# If you need to collect the return values check out 'futures'
pages_to_crawl.each do |page|
   worker_pool.process_page(page)
end

이번에는 5개의 스레드만 실행되고 완료되면 다음 항목을 선택합니다.

경주 조건 및 기타 위험

이것은 매우 멋지게 들릴지 모르지만 코드 전체에 스레드를 뿌리기 전에 동시 코드와 관련된 몇 가지 문제가 있음을 알아야 합니다.

예를 들어 스레드는 경쟁 조건에 취약합니다.

경합 조건 일이 잘못되어 엉망이 될 때입니다.

발생할 수 있는 또 다른 문제는 교착 상태입니다. 이것은 한 스레드가 일부 리소스에 대한 독점적 액세스(뮤텍스와 같은 잠금 시스템 사용)를 보유하고 이를 해제하지 않아 다른 모든 스레드에서 액세스할 수 없는 경우입니다.

이러한 문제를 피하려면 원시 스레드를 피하고 이미 세부 사항을 처리하는 일부 gem을 사용하는 것이 가장 좋습니다.

더 많은 스레딩 보석

우리는 이미 스레드 풀에 셀룰로이드를 사용했지만 확인해야 할 다른 동시성 중심 보석이 많이 있습니다.

  • https://github.com/grosser/parallel
  • https://github.com/chadrem/workers
  • https://github.com/ruby-concurrency/concurrent-ruby

알겠습니다. Ruby 스레드에 대해 한두 가지 배웠기를 바랍니다. !

이 기사가 유용했다면 공유하세요. 친구들도 배울 수 있도록 🙂