雷林鹏分享: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种状态:

  线程状态返回值

  Runnablerun

  SleepingSleeping

  Abortingaborting

  Terminated normallyfalse

  Terminated with exceptionnil

  线程和异常

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

  @[email protected]

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

  序号方法描述

  1Thread.abort_on_exception

  若其值为真的话,一旦某线程因异常而终止时,整个解释器就会被中断。它的默认值是假,也就是说,在通常情况下,若某线程发生异常且该异常未被Thread#join等检测到时,该线程会被无警告地终止。

  2Thread.abort_on_exception=

  如果设置为 true, 一旦某线程因异常而终止时,整个解释器就会被中断。返回新的状态

  3Thread.critical

  返回布尔值。

  4Thread.critical=

  当其值为true时,将不会进行线程切换。若当前线程挂起(stop)或有信号(signal)干预时,其值将自动变为false。

  5Thread.current

  返回当前运行中的线程(当前线程)。

  6Thread.exit

  终止当前线程的运行。返回当前线程。若当前线程是唯一的一个线程时,将使用exit(0)来终止它的运行。

  7Thread.fork { block }

  与 Thread.new 一样生成线程。

  8Thread.kill( aThread )

  终止线程的运行.

  9Thread.list

  返回处于运行状态或挂起状态的活线程的数组。

  10Thread.main

  返回主线程。

  11Thread.new( [ arg ]* ) {| args | block }

  生成线程,并开始执行。数会被原封不动地传递给块. 这就可以在启动线程的同时,将值传递给该线程所固有的局部变量。

  12Thread.pass

  将运行权交给其他线程. 它不会改变运行中的线程的状态,而是将控制权交给其他可运行的线程(显式的线程调度)。

  13Thread.start( [ args ]* ) {| args | block }

  生成线程,并开始执行。数会被原封不动地传递给块. 这就可以在启动线程的同时,将值传递给该线程所固有的局部变量。

  14Thread.stop

  将当前线程挂起,直到其他线程使用run方法再次唤醒该线程。

  线程实例化方法

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

  #!/usr/bin/ruby

  thr = Thread.new do # 实例化

  puts "In second thread"

  raise "Raise exception"

  end

  thr.join # 调用实例化方法 join

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

  序号方法描述

  1thr[ name ]

  取出线程内与name相对应的固有数据。 name可以是字符串或符号。 若没有与name相对应的数据时, 返回nil。

  2thr[ name ] =

  设置线程内name相对应的固有数据的值, name可以是字符串或符号。 若设为nil时, 将删除该线程内对应数据。

  3thr.abort_on_exception

  返回布尔值。

  4thr.abort_on_exception=

  若其值为true的话,一旦某线程因异常而终止时,整个解释器就会被中断。

  5thr.alive?

  若线程是"活"的,就返回true。

  6thr.exit

  终止线程的运行。返回self。

  7thr.join

  挂起当前线程,直到self线程终止运行为止. 若self因异常而终止时, 将会当前线程引发同样的异常。

  8thr.key?

  若与name相对应的线程固有数据已经被定义的话,就返回true

  9thr.kill

  类似于 Thread.exit 。

  10thr.priority

  返回线程的优先度. 优先度的默认值为0. 该值越大则优先度越高.

  11thr.priority=

  设定线程的优先度. 也可以将其设定为负数.

  12thr.raise( anException )

  在该线程内强行引发异常.

  13thr.run

  重新启动被挂起(stop)的线程. 与wakeup不同的是,它将立即进行线程的切换. 若对死进程使用该方法时, 将引发ThreadError异常.

  14thr.safe_level

  返回self 的安全等级. 当前线程的safe_level与$SAFE相同.

  15thr.status

  使用字符串"run"、"sleep"或"aborting" 来表示活线程的状态. 若某线程是正常终止的话,就返回false. 若因异常而终止的话,就返回nil。

  16thr.stop?

  若线程处于终止状态(dead)或被挂起(stop)时,返回true.

  17thr.value

  一直等到self线程终止运行(等同于join)后,返回该线程的块的返回值. 若在线程的运行过程中发生了异常, 就会再次引发该异常.

  18thr.wakeup

  把被挂起(stop)的线程的状态改为可执行状态(run), 若对死线程执行该方法时,将会引发ThreadError异常。

  (编辑:雷林鹏 来源:网络)

原文地址:https://www.cnblogs.com/pengpeng1208/p/10118918.html

时间: 2024-10-13 09:35:52

雷林鹏分享:Ruby 多线程的相关文章

雷林鹏分享:Ruby JSON

Ruby JSON 本章节我们将为大家介绍如何使用 Ruby 语言来编码和解码 JSON 对象. 环境配置 在使用 Ruby 编码或解码 JSON 数据前,我们需要先安装 Ruby JSON 模块.在安装该模块前你需要先安装 Ruby gem,我们使用 Ruby gem 安装 JSON 模块. 但是,如果你使用的是最新版本的 Ruby,可能已经安装了 gem,解析来我们就可以使用以下命令来安装Ruby JSON 模块: $gem install json 使用 Ruby 解析 JSON 以下为J

雷林鹏分享:Ruby CGI方法

Ruby CGI方法 以下为CGI类的方法列表: 序号方法描述 1CGI::new([ level="query"]) 创建 CGI 对象.query可以是以下值: query: 没有 HTML 生成输出 html3: HTML3.2 html4: HTML4.0 Strict html4Tr: HTML4.0 Transitional html4Fr: HTML4.0 Frameset 2CGI::escape( str) 使用 URL 编码来转义字符串 3CGI::unescape

雷林鹏分享:Ruby 异常

Ruby 异常 异常和执行总是被联系在一起.如果您打开一个不存在的文件,且没有恰当地处理这种情况,那么您的程序则被认为是低质量的. 如果异常发生,则程序停止.异常用于处理各种类型的错误,这些错误可能在程序执行期间发生,所以要采取适当的行动,而不至于让程序完全停止. Ruby 提供了一个完美的处理异常的机制.我们可以在 begin/end 块中附上可能抛出异常的代码,并使用 rescue 子句告诉 Ruby 完美要处理的异常类型. 语法 begin # - rescue OneTypeOfExce

雷林鹏分享:Ruby 面向对象

Ruby 面向对象 Ruby 是纯面向对象的语言,Ruby 中的一切都是以对象的形式出现.Ruby 中的每个值都是一个对象,即使是最原始的东西:字符串.数字,甚至连 true 和 false 都是对象.类本身也是一个对象,是 Class 类的一个实例.本章将向您讲解所有与 Ruby 面向对象相关的主要功能. 类用于指定对象的形式,它结合了数据表示法和方法,把数据整理成一个整齐的包.类中的数据和方法被称为类的成员. Ruby 类定义 当您定义一个类时,您实际是定义了一个数据类型的蓝图.这实际上并没

雷林鹏分享:Ruby File 类和方法

Ruby File 类和方法 File 表示一个连接到普通文件的 stdio 对象.open 为普通文件返回该类的一个实例. 类方法 序号方法 & 描述 1File::atime( path) 返回 path 的最后访问时间. 2File::basename( path[, suffix]) 返回 path 末尾的文件名.如果指定了 suffix,则它会从文件名末尾被删除. 例如:File.basename("/home/users/bin/ruby.exe") #=>

雷林鹏分享:Ruby 文件的输入与输出

Ruby 文件的输入与输出 Ruby 提供了一整套 I/O 相关的方法,在内核(Kernel)模块中实现.所有的 I/O 方法派生自 IO 类. 类 IO 提供了所有基础的方法,比如 read. write. gets. puts. readline. getc 和 printf. 本章节将讲解所有 Ruby 中可用的基础的 I/O 函数.如需了解更多的函数,请查看 Ruby 的 IO 类. puts 语句 在前面的章节中,您赋值给变量,然后使用 puts 语句打印输出. puts 语句指示程序

雷林鹏分享:Ruby 日期 &amp; 时间(Date &amp; Time)

Ruby 日期 & 时间(Date & Time) Time 类在 Ruby 中用于表示日期和时间.它是基于操作系统提供的系统日期和时间之上.该类可能无法表示 1970 年之前或者 2038 年之后的日期. 本教程将让您熟悉日期和时间的所有重要的概念. 创建当前的日期和时间 下面是获取当前的日期和时间的简单实例: #!/usr/bin/ruby -w time1 = Time.new puts "Current Time : " + time1.inspect # Ti

雷林鹏分享:Ruby 哈希(Hash)

Ruby 哈希(Hash) 哈希(Hash)是类似 "employee" => "salary" 这样的键值对的集合.哈希的索引是通过任何对象类型的任意键来完成的,而不是一个整数索引,其他与数组相似. 通过键或值遍历哈希的顺序看起来是随意的,且通常不是按照插入顺序.如果您尝试通过一个不存在的键访问哈希,则方法会返回 nil. 创建哈希 与数组一样,有各种不同的方式来创建哈希.您可以通过 new 类方法创建一个空的哈希: months = Hash.new 您也

雷林鹏分享:Ruby 模块(Module)

Ruby 模块(Module) 模块(Module)是一种把方法.类和常量组合在一起的方式.模块(Module)为您提供了两大好处. 模块提供了一个命名空间和避免名字冲突. 模块实现了 mixin 装置. 模块(Module)定义了一个命名空间,相当于一个沙箱,在里边您的方法和常量不会与其他地方的方法常量冲突. 语法 module Identifier statement1 statement2 ........... end 模块常量命名与类常量命名类似,以大写字母开头.方法定义看起来也相似: