Ruby 聊聊 MRI Ruby Concurrent [未完成]

killyfreedom · 发布于 2014年07月07日 · 最后由 douxiance 回复于 2015年08月20日 · 5470 次阅读
2376
本帖已被设为精华帖!

随着项目越做越大,对MRI Ruby的几种并发服务模型也有了一部分的了解,也抛出来和大家聊聊,希望有点新的收获.

众所周知,MRI Ruby是一个拥有GIL的Ruby实现,先天决定了在同一时刻,只会有一个线程被运行,虽然依然无法在根本上解决线程安全的问题,但是根本上来说,一个基础架构的偏向,会导致整体语言相关的社区的导向,简而言之,在ruby的世界里,绝大部分时间里,Thread,这个词,就是被忽略的命,纵使rails,也是才开始重视多线程的问题.

然而,开发越来越多,做的越来越多,慢慢发现,GIL真的是一个好大好大的限制,但,即使在这样的限制条件下,我们依然在尝试着各种不同的并发模型.

所有的并发模型,在MRI的ruby这个有GIL的实现下,最大的目的就是:

分离IO操作和CPU操作,让IO操作在执行的同时,CPU并不会堵塞在IO等待中,从而实现更高的程序效率.

由 function1 --- io1 --- function2 --- io2 --- 这样的执行策略变成: function1 -- function2 -- funtion1 --- function 2 io1 --------io2---------io1-----------io2----

用一个比较粗略的方式来比较下这几种并发服务模型的效率. 假设,我们认为每个function就是一个独立的事务,那么,在事务和事务之间切换的性能也代表了其不考虑IO干扰的最高的并行能力,也就是说,只考虑这几个模型下的Context Switch的能力.

做了这几年,用到的最多的,就这几种了:

  1. EventMachine
  2. MultiThread
  3. Celluloid

EventMachine是最早接触的了,本质上来说,EM是一个巨大的for循环,将IO操作独立具体事务之外,将控制权让渡于事件核心,由事件核心以事件的方式触发流程继续.

写了一个粗略的测试其切换能力的代码:

require 'eventmachine'
$c = 0
Thread.start do
  EM.run do
    p = proc {
      $c += 1
      EM.next_tick(p)
    }

    EM.next_tick(p)
  end
end

arr = []

20.times do
  puts $c
  arr << $c unless $c == 0
  $c = 0
  sleep 1
end

puts "avg: " + (arr.inject(0) { |r, i| r + i } / arr.length).to_s

得到的平均值: avg: 96511

EventMachin的缺点: callback太多...

我们想要的代码是:

do_a
do_b
do_c
do_d

而不是

do_a {
    do_b {
        do_c {
           do_d
        }
    }
}

然后就是MultiThread: 很神奇,在1.9之前,ruby甚至是不支持Native线程的,其线程创建在其VM之上,直到1.9之后,线程才真正使用了系统级别的线程实现,线程之间的调度,由用户级变成了内核级,从轻量级的实现变成了重量级的实现,切换速度下降了,倒是也带来了不少更接近与原生实践的能力.

#encoding: utf-8
require 'thread'
require 'irb'
$c = 0

2.times do
  Thread.new do
    while true do
        # $cv.wait#($m)
      $c += 1
        # $cv.signal
      Thread.pass
    end
  end
end
arr = []

20.times do
  puts $c
  arr << $c unless $c == 0
  $c = 0
  sleep 1
end

puts "avg: " + (arr.inject(0) { |r, i| r + i } / arr.length).to_s

结果:

# 1 thread, avg: 755507
# 2 thread, avg: 114600
# 3 thread, avg: 56838
# 4 thread, avg: 37229
# 5 thread, avg: 35603
# 100 thread, avg: 31825

困了...Celluloid等过两天再写....

共收到 11 条回复
1342

先给点个赞

2楼 已删除
60

jRuby 试过么?

96

EventMachine 也可以不写 callback 试试 em-synchrony

D77582

用多线程的话,可以考虑agent,用的是go的写法,代码上会自然很多

https://github.com/igrigorik/agent EM作者出品

115

Mark, 关注.

2145

最后一句可以写成: puts "avg: " + (arr.inject(:+)/ arr.length).to_s

14143

点个赞, 关注一把

3679

关注一下

12250

关注~

96

最近买了一本松本行宏的《代码的未来》,书中有讲到JRuby是支持多核的,而MRI也有支持多核的非线程安全的版本。如果想要通过MRI来实现多核编程,且在不存在内存共享的时候,可以选择非线程安全。

另外,EventMatchine是一个年代很久的gem。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册