初步讲解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种状态:

线程和异常

当某线程发生异常,且没有被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=@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

输出结果如下:

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(线程) 类方法如下:

线程实例化方法

以下实例调用了线程实例化方法 join:

#!/usr/bin/ruby

thr = Thread.new do  # 实例化
  puts "In second thread"
  raise "Raise exception"
end
thr.join  # 调用实例化方法 join

以下是完整实例化方法列表:

线程实例化方法

以下实例调用了线程实例化方法 join:

#!/usr/bin/ruby

thr = Thread.new do  # 实例化
  puts "In second thread"
  raise "Raise exception"
end
thr.join  # 调用实例化方法 join

以下是完整实例化方法列表:

(0)

相关推荐

  • 使用Ruby来处理JSON的简单教程

    环境配置 在使用 Ruby 编码或解码 JSON 数据前,我们需要先安装 Ruby JSON 模块.在安装该模块前你需要先安装 Ruby gem,我们使用 Ruby gem 安装 JSON 模块. 但是,如果你使用的是最新版本的 Ruby,可能已经安装了 gem,解析来我们就可以使用以下命令来安装Ruby JSON 模块: $gem install json 使用 Ruby 解析 JSON 以下为JSON数据,将该数据存储在 input.json 文件中: { "President":

  • 使用Ruby编写发送邮件的程序的简单教程

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式. Ruby提供了 Net::SMTP 来发送邮件,并提供了两个方法 new 和 start: new 方法有两个参数: server name 默认为 localhost port number 默认为 25 start 方法有以下参数: server - SMTP 服务器 IP, 默认为 localhost port - 端口号,默认

  • 在Ruby中利用Net::SMTP类发送电子邮件的教程

    简单邮件传输协议(SMTP)发送电子邮件及路由的e-mail邮件服务器之间的协议处理. Ruby 提供 Net::SMTP 类的简单邮件传输协议(SMTP)客户端的连接,并提供了两个新的方法:new 和 start. new 带两个参数: server name 默认为 localhost port number  默认为熟知的 25 start 方法带有以下这些参数: server - IP SMTP服务器名称,默认为localhost port - 端口号,默认为25 domain - 邮件

  • 初步讲解Ruby编程中的多线程

    每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程. 线程是程序中一个单一的顺序控制流程,在单个程序中同时运行多个线程完成不同的工作,称为多线程. Ruby 中我们可以通过 Thread 类来创建多线程,Ruby的线程是一个轻量级的,可以以高效的方式来实现并行的代码. 创建 Ruby 线程 要启动一个新的线程,只需要调用 Thread.new 即可: # 线程 #1 代码部分 Thread.new { # 线程 #2 执行代码 } # 线程 #1 执行代码 实例 以下实例展示了如何在

  • 举例初步讲解Ruby中的正则表达式

    正则表达式是一个特殊的字符序列可以帮助匹配或者找到其他字符串或串套,使用的模式保持一个专门的语法. 正则表达式文本是一个模式之间的斜线之间或任意分隔符 %r 如下: 语法: 复制代码 代码如下: /pattern/ /pattern/im    # option can be specified %r!/usr/local! # general delimited regular expression 例如: #!/usr/bin/ruby line1 = "Cats are smarter t

  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    0.线程的本质 线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度. 1.多线程: 使用多个处理句柄同时对多个任务进行控制处理的一种技术.据博主的理解,多线程就是该应用的主线程任命其他多个线程去协助它完成需要的功能,并且主线程和协助线程是完全独立进行的.不知道这样说好不好理解,后面慢慢在使用中会有更加详细的讲解. 2.多线程的使用: (1)最简单.最原始的使用方法:Thread oGetArgThre

  • Ruby编程中关于中断和返回的用法教程

    return,break,next 这几个关键字的使用都涉及到跳出作用域的问题,而他们的不同 则在于不同的关键字跳出去的目的作用域的不同,因为有代码块则导致有一些地方需要格外注意. return 常用方式 通常情况下的return语句和大家理解的意思是相同的. def m1 param if param == 1 return 'returned 1' end 'returned default value' # 根据Ruby语言规范,最后一条执行语句的结果将作为返回值返回,return是可选的

  • Ruby编程中的赋值相关操作

    在老版本的Ruby中,赋值语句的返回值是设置该属性的方法的返回值.在Ruby1.8中,赋值语句的值总是参数的值而方法的返回值将被丢掉. class Test def val=(val) @val = val return 99 end end t = Test.new a = t.val=2 a ->2 在老版本中,a将被赋值语句设置为99,而在Ruby1.8中,它的值为2. Ruby的赋值实际是以并行方式执行的,所以赋值语句右边的值不受赋值语句本身的影响.在左边的任意一个变量或者属性被赋值之前

  • Ruby编程中的语法使用风格推荐

    使用 :: 引用常量(包括类和模块)和构造器 (比如 Array() 或者 Nokogiri::HTML()).     永远不要使用 :: 来调用方法. # bad SomeClass::some_method some_object::some_method # good SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass() 使用括号将de

  • 举例讲解Swift编程中switch...case语句的用法

    Switch作为选择结构中必不可少的语句也被加入到了Swift中,只要有过编程经验的人对Switch语句都不会感到陌生,但苹果对Switch进行了大大的增强,使其拥有其他语言中没有的特性.使用Switch语句很简单: var i = 0 switch i { case 0: "0" //被输出 case 1: "1" default: "2" } 在这个最简单的Switch语句中,与其他语言很不同的一点是:不用显式的加上break.Swift的S

  • 深入讲解C#编程中嵌套类型和匿名类型的定义与使用

    嵌套类型 在类或结构内部定义的类型称为嵌套类型.例如: class Container { class Nested { Nested() { } } } 不管外部类型是类还是结构,嵌套类型均默认为 private,但是可以设置为 public.protected internal.protected.internal 或 private.在上面的示例中,Nested 对外部类型是不可访问的,但可以设置为 public,如下所示: class Container { public class N

  • 设计模式中的观察者模式在Ruby编程中的运用实例解析

    观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种. 在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知. 这通常透过呼叫各观察者所提供的方法来实现. 实现观察者模式的时候要注意,观察者和被观察对象之间的互动关系不能 体现成类之间的直接调用,否则就将使观察者和被观察对象之间紧密的耦合起来, 从根本上违反面向对象的设计的原则.无论是观察者"观察"观察对象, 还是被观察者将自己的改变"通知"观察者,都不应该直接调用.

  • 举例讲解C#编程中对设计模式中的单例模式的运用

    单例模式的介绍 说到单例模式,大家第一反应应该就是--什么是单例模式?,从"单例"字面意思上理解为--一个类只有一个实例,所以单例模式也就是保证一个类只有一个实例的一种实现方法罢了,下面给出单例模式的一个官方定义:确保一个类只有一个实例,并提供一个全局访问点.为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,以及后面也会剖析单例模式的实现思路: 为什么会有单例模式 看完单例模式的介绍,自然大家都会有这样一个疑问--为什么要有单例模式的?它在什么情况下使用的?从单例模式的

随机推荐