Ruby Ruby 中如何实现 spawn 线程?

zhenjunluo · 2012年12月06日 · 最后由 zhenjunluo 回复于 2012年12月12日 · 6677 次阅读
def hello
  Thread.new{
    5.times {
      sleep(1)
      puts "thread.."
    }
  }##怎样让该线程独立运行,即在hello执行完毕后不会影响该线程的运行

  sleep(2)
  puts "hello exit!"
end

hello
## 执行结果
# thread...
# hello exit!

#预期结果是输出5次thread..

目的:hello 运行结束后,puts "thread.."那个线程还在运行 (后台运行也可以),两个线程是互相独立的。也就是说创建出来的线程不会影响原来的线程,原来的线程也不会影响创建出来的线程。

下面是 java 的实现:

public class TestThread {

    public static void main(String[] args) {
      TestThread ts = new TestThread();
      Thread myThread = ts.new MyThead(100);

      myThread.start();
      System.out.println("main");

    }

    public class MyThead extends Thread {
        private long sleepTime;
        public MyThead (long sleepTime) {
          this.sleepTime = sleepTime;

        }

        public void run() {
          for (int i = 0 ; i < 10; i++) {
              System.out.println(this.getName() + "\t----- " + i + " -----");
              try {
                Thread.sleep(sleepTime);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            }
          }
        }

      }

运行结果:
main  #main线程退出
Thread-0    ----- 0 -----  #main中创建出来的线程继续运行
Thread-0    ----- 1 -----
Thread-0    ----- 2 -----
Thread-0    ----- 3 -----
Thread-0    ----- 4 -----
Thread-0    ----- 5 -----
Thread-0    ----- 6 -----
Thread-0    ----- 7 -----
Thread-0    ----- 8 -----
Thread-0    ----- 9 -----

就是想让一个线程马上返回,另一个线程继续执行任务

thread.join 让程序等待线程结束才退出

加个退出判断

hello
loop do
    sleep 1
    exit if Thread.list.count < 2
end 

#1 楼 @saiga 目的是不需要等待,两个线程互不关联,独立运行

#2 楼 @sevk 我是想实现这两个线程独立运行,互不影响,hello 可以马上结束,但是它创建出来的线程还能继续运行

#1 楼 @saiga 在一个函数内创建一个线程,这个函数结束了,创建出来的线程还能继续运行

#5 楼 @zhenjunluo 你这种情况是:子线程还没执行完毕,主线程已经退出。Ruby 的线程有点像 Java 的守护线程,主线程执行完毕所有的子线程都会销毁,所以如果要等待子线程执行完毕需要在程序出口之前调用 join 阻塞主线程

#6 楼 @saiga 对啊,我就想实现主线程退出,守护线程还在运行的功能。好像 java 中很容易实现这样的功能

#7 楼 @zhenjunluo 那就试试 fork 生成一个新的进程。

#9 楼 @reus 谢谢答复,我需要的是 thread,不是 process。ruby 的 drb 库创建出来的线程都能独立运行,就不知到怎么实现的

#8 楼 @saiga 在 rail 中使用多进程,这样不好,管理起来比较费事,而且会互相影响,比如它们都是共享同一个数据库 thread pool,一个进程的结束,会导致另一个进程的数据库连接失败

#4 楼 @zhenjunluo hello 执行之后,主进程如果不 thread.join 就会结束,那所有线程都会结束 线程就是这样的,你要达到目的要么 thread.join 要么 fork

@zhenjunluo 其实是两个问题:

  1. 主线程要等待子线程执行完才退出,答案 @saiga 已经给出了 Thread.join
  2. 主线程要后台运行,答案 @reus 已经给出了 daemon 进程

#10 楼 @zhenjunluo DRb 线程不退的主要原因是,主线程还没退出。

#12 楼 @reus

public class TestThread {

    public static void main(String[] args) {
      TestThread ts = new TestThread();
      Thread myThread = ts.new MyThead(100);

      myThread.start();
      System.out.println("main");

    }

    public class MyThead extends Thread {
        private long sleepTime;
        public MyThead (long sleepTime) {
          this.sleepTime = sleepTime;

        }

        public void run() {
          for (int i = 0 ; i < 10; i++) {
              System.out.println(this.getName() + "\t----- " + i + " -----");
              try {
                Thread.sleep(sleepTime);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            }
          }
        }

      }

运行结果:
main
Thread-0    ----- 0 -----
Thread-0    ----- 1 -----
Thread-0    ----- 2 -----
Thread-0    ----- 3 -----
Thread-0    ----- 4 -----
Thread-0    ----- 5 -----
Thread-0    ----- 6 -----
Thread-0    ----- 7 -----
Thread-0    ----- 8 -----
Thread-0    ----- 9 -----

这是 java 中的实现

#13 楼 @luikore 可能是我表达的不太清楚,写了个 java 版本的(见原帖更新),想实现一样的功能

@zhenjunluo 可能你没看明白楼上说的,我把最后代码弄好给你看

def hello
  thread = Thread.new{
    5.times {
      sleep(1)
      puts "thread.."
    }
  }

  sleep 2
  puts "hello exit!"
  thread
end

thread = hello()
thread.join # 主线程结束后, 程序就结束了, 如果要等待其他线程结束后再结束主线程, 要调用 join

如果你在一个长期执行的程序里创建 thread, 例如 rails 里,就不需要 thread.join

#18 楼 @luikore 你可能没明白我的意思,在 rails 中,前端发来一个请求,我在 action 中创建出一个线程,此时我需要马上给前端返回,创建出来的线程在后台运行,如果用 jion,那么我需要等待到所有线程运行完,这样才能返回给前端,我需要马上返回

#19 楼 @zhenjunluo 在 rails 中,你就不需要调用 join 了

#19 楼 @zhenjunluo 你原来的程序已经创建了线程并且平行于主线程运行了,问题是 ruby 线程和 java 线程不一样:java 进程会等待所有线程结束才结束,ruby 主线程结束了进程就结束了,进程结束时所有其他线程都一起完蛋。这就是为什么这个 hello 程序里看不到想要的效果,为了看到效果,别人才建议你用 thread.join 去等待 hello() 创建的线程结束。

但如果你一开始就说明白是 rails 里创建的线程,那又是另一回事了。rails 的主线程一直存在,不用调用 thread.join 去等待 hello() 创建的线程结束。

#19 楼 @zhenjunluo 这种情况你应该用任务队列来处理,不然一个 siege 或者 ab 就可以把你的服务器拖得死死的

#15 楼 @zhenjunluo java 能这样是因为 jvm 会等待所有的线程结束,也就是 jvm 帮你做了 join。ruby 没有,所以你要自己调用

#19 楼 @zhenjunluo 真心建议不要这么做,不知道 Ruby 怎么新建线程和切换上下文,不过代价应该是不会少,你这样用绝对死得飞快。建议像@reus 说的,采用任务队列。 话说,好像没看到 ruby vm 相关的书籍呢,现在都是用 jvm 原理来想当然的胡扯 = =

#21 楼 @luikore 嗯,谢谢你的回答。之前在 rails 中怎么试都不行,换了个环境又可以了,很奇怪

#23 楼 @reus #24 楼 @saiga 网上是这么说的,ruby 解释器一退出,就会杀死所有的线程,不管它们的死活。如果采用任务队列的话,那么会存在延时,也就是说这个任务可能会等待几秒中然后才会执行。

我目前的使用场景是,前端发来一个请求,我要马上返回,让它把页面渲染出来,同时有线程在后面获取数据,时间有时会很长,10s 以上,如果顺序执行,那么在最糟糕的情况下,这个页面渲染出来需要很长时间;如果等页面渲染完在发请求来获取数据,这样又慢了一段时间! 目前我使用 ruby 的 drb 库来实现,在后台启动一个 drb server,在 action 中发一个请求给 drb server,创建线程的事情就交给它了,action 也可以马上返回。这样的缺点是如果并发大的话,创建的线程就会很多

不知到你们有没有好的方法,或者能够保证队列中的任务立马运行

#26 楼 @zhenjunluo 用 thread pool 替代 drb,这样不会动态创建线程,参考:http://burgestrand.se/articles/quick-and-simple-ruby-thread-pool.html

#27 楼 @reus 嗯,好的,谢谢啊

@zhenjunluo 搞太复杂了!页面的咚咚可以静态化或者提前缓存好,请求和加载数据的活交给 js 吧,ruby 只负责把处理完的结果通过 json 反馈给 js,这些线程、进程的复杂事情都交给 web server 和 app server 去干,你的线程处理得再好也不如一个成熟的 web server 能力强。

@zhenjunluo 简单说,就是把 rails 搞成一个 rails-api,页面展现别揉在 rails 里面,把页面前端做成 webapps,今后可以直接换成 iOS/Android 等 Native Apps,后端基本不用改动。

#26 楼 @zhenjunluo 。。。我怎么觉得一个 resque 就能解决这问题

rails 是永远运行的,所以加个 loop do 吧 :

hello
loop do
    sleep 1    
end 

或这样:

hello
sleep 9999999999999999

#31 楼 @jjym resque 会有延迟,除非你能保证里面的任务一创建出来就立即执行

#33 楼 @zhenjunluo 你26#都说缺点是并发大的时候线程会多,这个本来就是矛盾的,既然队列有延迟就代表任务没执行完,这种情况如果是线程肯定也会创建很多。折中下,就是耗时不是很久的任务多分配几个worker尽量保证速度,很慢才能执行完的任务就让他慢下去。。

#34 楼 @jjym 延迟我我指的是任务加入队列到取出来执行这段时间,因为要跟前端交互,所以我希望这段时间尽量短。

#30 楼 @zeeler 你的意思是吧 views 分离出来吗?有没有什么开源的代码,供学习下,谢谢

#36 楼 @zhenjunluo 不需要开源呀,比如 rails 里面只做 api,views 部分不放在 rails 里面,单独写,只包含 HTML/CSS/JS

#37 楼 @zeeler 这个是个好注意,tanks

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