English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
시스템에서 실행 중인 모든 프로그램은 하나의 프로세스입니다. 각 프로세스는 하나나 여러 스레드를 포함합니다.
스레드는 프로그램에서 하나의 순차적인顺序 제어 프로세스로, 여러 스레드가 동시에 여러 작업을 수행하여 다중 스레드라고 합니다.
Ruby에서는 Thread 클래스를 통해 다중 스레드를 생성할 수 있습니다. Ruby의 스레드는 가벼운 중량의 스레드로, 병行的 코드를 효율적으로 구현할 수 있습니다.
새로운 스레드를 시작하려면 Thread.new만 호출하면 됩니다:
# 스레드 #1 코드 부분 Thread.new { # 스레드 #2 코드 실행 } # 스레드 #1 코드 실행
다음 예제는 Ruby 프로그램에서 다중 스레드를 사용하는 방법을 보여줍니다:
#!/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}" t1Thread.new{func1()} t2Thread.new{func2()} t1.join t2.join puts "End at #{Time.now}"
위 코드의 실행 결과는 다음과 같습니다:
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
1스레드의 생성은 Thread.new를 사용할 수 있으며, Thread.start 또는 Thread.fork와 같은 동일한 문법으로 Thread.new, Thread.start, Thread.fork를 사용할 수 있습니다.
2스레드를 생성한 후에는 시작하지 않아도 됩니다. 스레드는 자동으로 실행됩니다.
3스레드 클래스는 스레드를 제어하기 위한 몇 가지 메서드를 정의합니다. 스레드는 Thread.new에서의 코드 블록을 실행합니다.
4스레드 코드 블록의 마지막 문장은 스레드의 값이며, 스레드 메서드를 통해 호출할 수 있습니다. 스레드가 실행되면 스레드 값이 반환되며, 스레드가 실행되지 않으면 값을 반환하지 않습니다.
5Thread.current 메서드는 현재 스레드를 나타내는 객체를 반환합니다. Thread.main 메서드는 메인 스레드를 반환합니다.
6Thread.Join 메서드를 통해 스레드를 실행하면, 이 메서드는 메인 스레드를 대기시키고 현재 스레드가 완료될 때까지 대기합니다.
스레드는5다음과 같은 상태가 있습니다:
스레드 상태 | 반환 값 |
---|---|
실행가능 | run |
수면 | Sleeping |
종료 | aborting |
정상 종료 | false |
예외로 인한 종료 | nil |
특정 스레드가 예외가 발생했지만 rescue로 잡히지 않았을 때, 이 스레드는 일반적으로 경고 없이 종료됩니다. 하지만 다른 스레드가 Thread#join으로 이 스레드를 기다리고 있다면, 기다리는 스레드도 동일한 예외가 발생합니다.
begin t = Thread.new do Thread.pass # 메인 스레드가确实是join을 기다리고 있습니다 raise "unhandled exception" end t.join rescue p $! # => "unhandled exception" end
다음과 같이 사용하면3이 메서드를 사용하면 특정 스레드가 예외로 인해 종료될 때 인터프리터가 중지될 수 있습니다.
스크립트 실행 시 지정-d이 옵션을 통해 디버깅 모드로 실행할 수 있습니다.
Thread.abort_on_exception로 표지를 설정합니다.
Thread#abort_on_exception를 통해 지정된 스레드에 표지를 설정합니다.
위와 같이 사용할 때3이 방법 중 하나가 선택되면 전체 인터프리터가 중지됩니다.
t = Thread.new { ... } t.abort_on_exception = true
Ruby에서는 동기화를 구현하는 세 가지 방법을 제공합니다.
1. Mutex 클래스를 통해 스레드 동기화 구현
2. 데이터 전달을 모니터링하는 Queue 클래스를 통해 스레드 동기화 구현
3. ConditionVariable를 사용하여 동기화 제어
Mutex 클래스를 통해 스레드 동기화 제어를 구현합니다. 여러 스레드가 동시에 특정 프로그램 변수를 필요로 할 때, 이 변수의 일부를 lock으로 잠금할 수 있습니다. 코드는 다음과 같습니다:
#!/usr/bin/ruby require "thread" puts "동기화 스레드" @num=200 @mutex=Mutex.new def buyTicket(num) @mutex.lock if @num>=num @num=@num-num puts "귀하가 성공적으로 #{num} 티켓을 구매했습니다" else puts "죄송합니다, 티켓이 충분하지 않습니다" 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
위 코드의 실행 결과는 다음과 같습니다:
동기화 스레드 귀하가 성공적으로 구매했습니다 15 tickets 귀하가 성공적으로 구매했습니다 20 티켓 귀하가 성공적으로 구매했습니다 15 tickets 귀하가 성공적으로 구매했습니다 20 티켓 귀하가 성공적으로 구매했습니다 15 tickets 귀하가 성공적으로 구매했습니다 20 티켓 귀하가 성공적으로 구매했습니다 15 tickets 귀하가 성공적으로 구매했습니다 20 티켓 귀하가 성공적으로 구매했습니다 15 tickets 귀하가 성공적으로 구매했습니다 20 티켓 귀하가 성공적으로 구매했습니다 15 tickets 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다 죄송합니다, 티켓이 충분하지 않습니다
lock 변수를 사용하는 것 외에도 try_lock 변수를 사용할 수 있으며, 또한 Mutex.synchronize를 사용하여 특정 변수에 대한 접근을 동기화할 수 있습니다.
Queue 클래스는 스레드를 지원하는 큐를 나타내며, 큐의 마지막에 대한 동기화된 접근을 제공합니다. 다른 스레드는 동일한 클래스를 사용할 수 있지만, 이 큐에서의 데이터가 동기화되는지에 대해 걱정하지 않아도 됩니다. 또한, SizedQueue 클래스를 사용하여 큐의 길이를 제한할 수 있습니다.
SizedQueue 클래스는 매우 편리하게 우리가 스레드 동기화 애플리케이션을 개발하는 데 도움을 줄 수 있습니다. 왜냐하면 이 큐에 추가되면 스레드 동기화 문제에 대해 걱정하지 않아도 됩니다.
经典的生产者消费者问题:
#!/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
프로그램의 출력 결과는 다음과 같습니다:
SizedQuee 테스트 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는 자신의 비밀 변수를 가질 수 있으며, Thread의 비밀 변수는 Thread가 생성될 때 입력됩니다. Thread 범위 내에서 사용할 수 있지만, Thread 외부에서 공유되지 않습니다.
하지만, 때로는 Thread의 지역 변수가 다른 Thread나 메인 Thread에 의해 접근되어야 할 때는 어떻게 해야 하나요? Ruby는 이름을 통해 Thread 변수를 생성할 수 있으며, Thread를 Hash 스타일의 해시 테이블로 볼 수 있습니다. []=를 통해 데이터를 입력하고, []를 통해 데이터를 출력할 수 있습니다. 아래의 코드를 보겠습니다:
#!/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}"
위 코드가 실행된 결과 출력 결과는 다음과 같습니다:
8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10
메인 Thread는 서브 Thread가 실행되기를 기다리고, 각 값을 나누어 출력합니다. 。
Thread의 prioritiy는 Thread의 스케줄링에 영향을 미치는 주요 요소입니다. 다른 요소로는 CPU 사용 시간의 길이, Thread 그룹 스케줄링 등이 있습니다.
Thread.priority 메서드를 사용하여 Thread의 prioritiy를 얻을 수 있으며, Thread.priority= 메서드를 사용하여 prioritiy를 조정할 수 있습니다.
Thread의 기본 prioritiy는 0입니다. prioritiy가 높은 Thread는 더 빠르게 실행됩니다.
한 Thread는 자신의 범위 내의 모든 데이터에 접근할 수 있지만, 특정 Thread 내에서 다른 Thread의 데이터에 접근할 필요가 있을 때 어떻게 해야 하나요? Thread 클래스는 Thread 데이터 간의相互 접근 방법을 제공하며, 단순히 한 Thread를 Hash 테이블로 사용하여 데이터를 입력하거나 출력할 수 있습니다.
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"]}"}
thread를 해시 테이블로 사용하여 []와 []= 메서드를 통해 스레드 간의 데이터 공유를 구현할 수 있습니다.
Mutex( Mutual Exclusion =互斥锁)는 다중 스레드 프로그래밍에서, 두 개의 스레드가 동시에 동일한 공동 자원(예: 전역 변수)을 읽고 쓰지 못하도록 하는 메커니즘입니다.
#!/usr/bin/ruby require 'thread' count1 =2 = 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}"
위의 예제의 실행 결과는 다음과 같습니다:
count1 : 9712487 count2 : 12501239 difference : 0
#!/usr/bin/ruby require 'thread' mutex = Mutex.new count1 =2 = 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}"
위의 예제의 실행 결과는 다음과 같습니다:
count1 : 1336406 count2 : 1336406 difference : 0
두 개 이상의 연산 단위가 상호간에 상대방이 실행을 중지하도록 기다리며 시스템 자원을 얻으려고 하지만 어떤一方도 미리 탈출하지 않으면서 이 상태는死锁이라고 합니다.
예를 들어, 프로세스 p1디스플레이어를 사용하면서 또한 프린터를 사용해야 하지만 프린터는 프로세스 p에 의해 사용되고 있습니다.2占用,p2또한 디스플레이어를 사용해야 하므로死锁이 발생합니다.
Mutex 객체를 사용할 때는死锁에 주의해야 합니다.
#!/usr/bin/ruby require 'thread' mutex = Mutex.new cv = ConditionVariable.new a = Thread.new { mutex.synchronize { puts "A: 중요 구간을 가지고 있지만 cv를 기다리겠습니다" cv.wait(mutex) puts "A: 중요 구간을 다시 얻었습니다! 제가 통치합니다!" } } puts "(후에, 랜치로 돌아옴...)" b = Thread.new { mutex.synchronize { puts "B: 지금 저는 중요한 섹션입니다, cv와는 끝내었습니다" cv.signal puts "B: 저는 여전히 중요한 섹션입니다, 완료하고 있습니다" } } a.join b.join
위의 예제 출력 결과는 다음과 같습니다:
A: 저는 중요한 섹션을 가지고 있지만 cv를 기다릴 것입니다 (나중에, 농장으로 돌아와서...) B: 지금 저는 중요한 섹션입니다, cv와는 끝내었습니다 B: 저는 여전히 중요한 섹션입니다, 완료하고 있습니다 A: 저는 다시 중요한 섹션을 가지고 있습니다! 저는 통치합니다!
Thread(스레드) 클래스의 메서드는 다음과 같습니다:
순번 | 메서드 설명 |
---|---|
1 | Thread.abort_on_exception 값이 참이면, 어떤 스레드가 예외로 인해 종료되면 전체 해석기가 중지됩니다. 기본 값은 거짓으로, 즉 일반적으로 어떤 스레드가 예외가 발생했지만 이 예외가 Thread#join 등에 의해 검출되지 않았을 때, 그 스레드는 경고 없이 종료됩니다. |
2 | Thread.abort_on_exception= 만약 설정되면 true어떤 스레드가 예외로 인해 종료되면, 전체 해석기가 중지됩니다. 새로운 상태를 반환합니다. |
3 | Thread.critical 부울 값을 반환합니다. |
4 | Thread.critical= 값이 true일 때, 스레드 전환은 수행되지 않습니다. 현재 스레드가 중지(stop) 상태이거나 신호(signal)이 중간에 개입할 때, 값이 자동으로 false로 변경됩니다. |
5 | Thread.current 현재 실행 중인 스레드(현재 스레드)를 반환합니다. |
6 | Thread.exit 현재 스레드의 실행을 종료하고 현재 스레드를 반환합니다. 현재 스레드가 유일한 스레드라면 exit(0)을 사용하여 실행을 종료합니다. |
7 | Thread.fork { block } Thread.new와 같이 스레드를 생성합니다. |
8 | Thread.kill(aThread) 스레드의 실행을 종료합니다. |
9 | Thread.list 실행 중이거나 대기 상태인 활성 스레드의 배열을 반환합니다. |
10 | Thread.main 메인 스레드로 돌아갑니다. |
11 | Thread.new([arg])* ) {| args | block } 스레드를 생성하고 실행을 시작합니다. 값은 그대로 블록에 전달됩니다. 이를 통해 스레드를 시작하면서 해당 스레드의固有 지역 변수에 값을 전달할 수 있습니다. |
12 | Thread.pass 다른 스레드에 실행 권한을 넘겨줍니다. 실행 중인 스레드의 상태는 변경되지 않으며, 대신 다른 실행할 수 있는 스레드에 컨트롤을 넘겨줍니다(명시적인 스레드 조정). |
13 | Thread.start( [ args ])* ) {| args | block } 스레드를 생성하고 실행을 시작합니다. 값은 그대로 블록에 전달됩니다. 이를 통해 스레드를 시작하면서 해당 스레드의固有 지역 변수에 값을 전달할 수 있습니다. |
14 | Thread.stop 현재 스레드를 일시적으로 중지하고, 다른 스레드가 run 메서드를 사용하여 다시 깨우면 깨어났습니다. |
다음 예제는 스레드 예제 메서드 join을 호출합니다:
#!/usr/bin/ruby thr = Thread.new do # 메서드 예제 puts " In second thread " raise " Raise exception " end thr.join # 메서드 호출 예제 join
다음은 전체 메서드 목록입니다:
순번 | 메서드 설명 |
---|---|
1 | thr[ name ] 스레드 내에서 name과 일치하는 스레드固有 데이터를 꺼냅니다. name은 문자열이나 기호일 수 있습니다. name과 일치하는 데이터가 없을 경우 nil을 반환합니다. |
2 | thr[ name ]= 스레드 내에서 name과 일치하는 스레드固有 데이터의 값을 설정합니다. name은 문자열이나 기호일 수 있습니다. nil로 설정하면, 해당 스레드 내에서 해당 데이터를 제거합니다. |
3 | thr.abort_on_exception 부울 값을 반환합니다. |
4 | thr.abort_on_exception= 이 값이 true라면, 어떤 스레드가 예외로 종료되면 전체 인터프리터가 중지됩니다. |
5 | thr.alive? 스레드가 "활성"인 경우 true를 반환합니다. |
6 | thr.exit 스레드의 실행을 종료합니다. self을 반환합니다. |
7 | thr.join 현재 스레드를 일시적으로 중지하고, self 스레드가 실행을 종료할 때까지 기다립니다. self이 예외로 종료되면 현재 스레드에도 같은 예외가 발생합니다。 |
8 | thr.key? name과 일치하는 스레드固有 데이터가 이미 정의되어 있다면 true를 반환합니다 |
9 | thr.kill 유사합니다 Thread.exit 。 |
10 | thr.priority 스레드의 우선순위를 반환합니다. 기본 우선순위 값은 0입니다. 이 값이 클수록 우선순위가 높습니다. |
11 | thr.priority= 스레드의 우선순위를 설정합니다. 음수를 설정할 수도 있습니다. |
12 | thr.raise( anException ) 이 스레드 내에서 강제로 예외를 발생시킵니다. |
13 | thr.run 挂起的(stop)线程重新启动. wakeup와 달리, 즉시 스레드 전환을 수행합니다. 사망한 프로세스에 대해 이 메서드를 사용할 경우 ThreadError 예외가 발생합니다. |
14 | thr.safe_level self의 보안 수준을 반환합니다. 현재 스레드의 safe_level은 $SAFE와 같습니다. |
15 | thr.status 활성 스레드의 상태를 나타내기 위해 문자열 "run", "sleep" 또는 "aborting"을 사용합니다. 스레드가 정상적으로 종료된 경우 false를 반환합니다. 예외로 종료된 경우 nil을 반환합니다. |
16 | thr.stop? 스레드가 종료 상태(dead)이거나挂起(stop) 상태이면 true를 반환합니다. |
17 | thr.value self 스레드가 종료될 때까지 기다리며, 그 후 스레드 블록의 반환 값을 반환합니다. 스레드의 실행 중에 예외가 발생하면 해당 예외가 다시 발생합니다. |
18 | thr.wakeup 挂起(stop)된 스레드의 상태를 실행 가능한 상태(run)으로 변경합니다. 이 메서드를 사망 스레드에 대해 호출할 경우 ThreadError 예외가 발생합니다. |