Ruby 多线程学习与实践

Ruby 多线程

每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。

线程是程序中一个单一的顺序控制流程,在单个程序中同时运行多个线程完成不同的工作,称为多线程。

Ruby 中我们可以通过 Thread 类来创建多线程,Ruby的线程是一个轻量级的,可以以高效的方式来实现并行的代码。


创建 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}"
t1=Thread.new{func1()}
t2=Thread.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这三个方法来创建线程。

2、创建线程后无需启动,线程会自动执行。

3、Thread 类定义了一些方法来操控线程。线程执行Thread.new中的代码块。

4、线程代码块中最后一个语句是线程的值,可以通过线程的方法来调用,如果线程执行完毕,则返回线程值,否则不返回值直到线程执行完毕。

5、Thread.current 方法返回表示当前线程的对象。 Thread.main 方法返回主线程。

6、通过 Thread.Join 方法来执行线程,这个方法会挂起主线程,直到当前线程执行完毕。


线程状态

线程有5种状态:

线程状态 返回值
Runnable run
Sleeping Sleeping
Aborting aborting
Terminated normally false
Terminated with exception 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类实现线程同步

通过Mutex类实现线程同步控制,如果在多个线程钟同时需要一个程序变量,可以将这个变量部分使用lock锁定。 代码如下:

#encoding:gbk
require "thread"
puts "Synchronize Thread"

@num=200
@mutex=Mutex.new

def buyTicket(num)
    @mutex.lock
        if @num>=num
            @num[email protected]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

输出结果如下:

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

除了使用lock锁定变量,还可以使用try_lock锁定变量,还可以使用Mutex.synchronize同步对某一个变量的访问。

监管数据交接的Queue类实现线程同步

Queue类就是表示一个支持线程的队列,能够同步对队列末尾进行访问。不同的线程可以使用统一个对类,但是不用担心这个队列中的数据是否能够同步,另外使用SizedQueue类能够限制队列的长度

SizedQueue类能够非常便捷的帮助我们开发线程同步的应用程序,应为只要加入到这个队列中,就不用关心线程的同步问题。

经典的生产者消费者问题:

#encoding:gbk
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 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

使用ConditionVariable实现同步控制

使用 ConditonVariable进行同步控制,能够在一些致命的资源竞争部分挂起线程直到有可用的资源为止。

#encoding:gbk
require "thread"
puts "thread synchronize by ConditionVariable"

mutex = Mutex.new
resource = ConditionVariable.new

a = Thread.new {
    mutex.synchronize {
        # 这个线程目前需要resource这个资源
        resource.wait(mutex)
        puts "get resource"
    }
}

b = Thread.new {
    mutex.synchronize {
        #线程b完成对resourece资源的使用并释放resource
        resource.signal
    }
}

a.join
puts "complete"

mutex 是声明的一个资源,然后通过ConditionVariable来控制申请和释放这个资源。

b 线程完成了某些工作之后释放资源resource.signal,这样a线程就可以获得一个mutex资源然后进行执行。 执行结果:

 thread synchronize by ConditionVariable get resource complete 

线程类方法

完整的 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线程终止运行(等同于join)后,返回该线程的块的返回值. 若在线程的运行过程中发生了异常, 就会再次引发该异常.
18 thr.wakeup
把被挂起(stop)的线程的状态改为可执行状态(run), 若对死线程执行该方法时,将会引发ThreadError异常

作为新接触的Ruby新手,不足之处,还请各位大神指点...

时间: 2024-08-27 05:52:02

Ruby 多线程学习与实践的相关文章

Google App Engine 学习和实践

这个周末玩了玩Google App Engine,随手写点东西,算是学习笔记吧.不当之处,请多多指正. 作者:liigo,2009/04/26夜,大连 原创链接:http://blog.csdn.net/liigo/archive/2009/04/26/4127055.aspx 转载请注明出处:http://blog.csdn.net/liigo 一,怎么想起来玩Google App Engine了呢? 近期想写一个小程序,以便在公司的电脑和家里的电脑之间随时共享数据.但是没有现成的server

Java多线程学习(吐血超详细总结)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程

Qt多线程学习:创建多线程

[为什么要用多线程?] 传统的图形用户界面应用程序都仅仅有一个运行线程,而且一次仅仅运行一个操作.假设用户从用户界面中调用一个比較耗时的操作,当该操作正在运行时,用户界面一般会冻结而不再响应.这个问题能够用事件处理和多线程来解决. [Linux有线程的概念吗?] 传统的UNIX系统也支持线程的概念,但一个进程里仅仅同意有一个线程,这样多线程就是多进程.Linux下的Posix线程(pthreads)是一种轻量级的进程的移植性实现,线程的调度由内核完毕,每一个线程都有自己的编号.假设使用线程,整体

Java多线程学习(详细)

一.进程与线程的区别 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小.(线程是cpu调度的最小单位) 线程和进程一样分为五个阶段:创建.就绪.运行.阻塞.终止.     多进程是指操作系统能同时运行多个任务(程序).     多线程是指在同一程序中有多个顺序流在执行. 在java中要想实现多线程,有两种手段,一

C#多线程学习(一) 多线程的相关概念

什么是进程?    当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程?    线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数. 什么是多线程?    多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务. 多线程的好处:    可以提

C#多线程学习(六) 互斥对象

C#多线程学习(六) 互斥对象 如何控制好多个线程相互之间的联系,不产生冲突和重复,这需要用到互斥对象,即:System.Threading 命名空间中的 Mutex 类. 我们可以把Mutex看作一个出租车,乘客看作线程.乘客首先等车,然后上车,最后下车.当一个乘客在车上时,其他乘客就只有等他下车以后才可以上车.而线程与Mutex对象的关系也正是如此,线程使用Mutex.WaitOne()方法等待Mutex对象被释放,如果它等待的Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mute

C#多线程学习(一) 多线程的相关概念(转)

什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程?线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数. 什么是多线程?多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务. 多线程的好处:可以提高CPU的利用率.在多线程程序中

多线程学习 + o2o简识

多线程学习: ( 1.NSThread 2.NSOperationQueue 3.GCD ) 1.进程和线程: 进程:app无法独立运行,需要分配内存空间,每个app至少有一个进程,是应用程序的开始(缺点:不能同时执行) 线程:是应用程序运行的最小单元可以多个线程并发同时执行,防止主线程堵塞,增加运行效率. 主线程:又叫UI主线程,程序运行都是在主线程加载,加载视图,但不可加载数据,因为请求网络数据的时间特别长,会出现空白现象(更新UI一定要在主线程中写) 子线程:没法加载UI,UI只在主线程中

hadoop2.5.2学习及实践笔记(四)—— namenode启动过程源码概览

对namenode启动时的相关操作及相关类有一个大体了解,后续深入研究时,再对本文进行补充 >实现类 HDFS启动脚本为$HADOOP_HOME/sbin/start-dfs.sh,查看start-dfs.sh可以看出,namenode是通过bin/hdfs命令来启动 $ vi start-dfs.sh # namenodes NAMENODES=$($HADOOP_PREFIX/bin/hdfs getconf -namenodes) echo "Starting namenodes o