2013-06-18 66 views
0

我在构建Ruby应用程序。我有一组我想要灰度的图像。我以前是这样的代码:Ruby多线程性能问题

def Tools.grayscale_all_frames(frames_dir,output_dir) 
    number_of_frames = get_frames_count(frames_dir) 
    img_processor = ImageProcessor.new(frames_dir) 
    create_dir(output_dir) 

    for i in 1..number_of_frames 
     img_processor.load_image(frames_dir+"/frame_%04d.png"%+i) 
     img_processor.greyscale_image 
     img_processor.save_image_in_dir(output_dir,"frame_%04d"%+i) 
    end 
end 

线程的代码之后:

def Tools.greyscale_all_frames_threaded(frames_dir,output_dir) 
    number_of_frames = get_frames_count(frames_dir) 
    img_processor = ImageProcessor.new(frames_dir) 
    create_dir(output_dir) 
    greyscale_frames_threads = [] 

    for frame_index in 1..3 
     greyscale_frames_threads << Thread.new(frame_index) { |frame_number| 
      puts "Loading Image #{frame_number}" 
      img_processor.load_image(frames_dir+"/frame_%04d.png"%+frame_number) 
      img_processor.greyscale_image 
      img_processor.save_image_in_dir(output_dir,"frame_%04d"%+frame_number) 
      puts "Greyscaled Image #{frame_number}" 
     } 
    end 

    puts "Starting Threads" 
    greyscale_frames_threads.each { |thread| thread.join } 

end 

我所期待的是正在催生对每个图像的线程。我有1000张图片。分辨率是1920 * 1080。所以我如何看待事情就是这样。我有一个线程数组,我称之为.join。因此,加入将采取所有线程,并一个接一个地启动它们?这是否意味着它会等到线程1完成,然后启动线程2?那么多线程的要点是什么?

我想是这样的:

运行所有在同一时间线程和其他后没有之一。在数学上,它将在完成1帧的同一时间内完成所有1000帧,对吗?

也可以有人解释我什么。加入呢? 从我的理解.join将停止主线程,直到你的线程完成? 如果你不使用.join,那么线程将运行后台,主线程将继续。

那么使用.join有什么意义?我想让我的主线程继续运行,并让后台的其他线程执行某些操作?

感谢您的任何帮助/澄清!

+2

也许你应该看看'resque'这样的背景工具工具,如数学上的“ –

+1

” - 如果创建线程花费0个时间,并且你有足够的CPU周期和RAM,那么是的。在实践中,这应该会严重损坏您的电脑。 – Dogbert

+1

*它会在完成1帧的同时完成所有1000帧,对吗?*给定1000个内核和一些内存,是的。 – Stefan

回答

0

那么join将采取所有的线程,并一个接一个地启动它们?

不,线程在调用Thread#new时启动。它创建一个新线程并执行该线程内的给定块。

join将停止主线程,直到您的线程完成?

是的,它会挂起执行,直到接收者(你的每个线程)存在。

那么使用join有什么意义?

有时候你想开始在平行一些任务,但你必须等待每个任务完成后才能继续。

我希望我的主线程可以继续运行,并在后台的其他线程做的东西

那就不叫join

毕竟,并行启动1,000个线程并不是一个好主意。只有当CPU可用时,您的机器才能够并行运行多个任务。因此,不是启动1,000个线程,而是将您的作业/任务放入队列/池中,并使用一些工作线程(CPU数=工作人员数)处理它们。

+0

确定现在有道理!非常感谢! –

3

只有当您拥有1000个CPU核心和大量RAM(读取:数百和数百)RAM时,才会出现这种情况。

连接点不是启动线程,而是等待线程完成。所以在一个线程数组上调用join是等待它们完成的一种常见模式。

解释所有这些,并澄清你的误解,这需要深入挖掘。在C/Assembler级别,mst现代操作系统(Win,Mac,Linux和其他一些操作系统)使用抢先式调度程序。如果你只有一个核心,两个以并列方式运行的程序是完全错觉的。实际上,内核每隔几毫秒就会在两个时间之间进行切换,这给所有使用缓慢处理的人造成了并行处理的错觉。

在更新,更现代化的CPU中,通常有多个核心。目前最强大的CPU可以达到(我认为)16个真实内核+16个超线程内核(请参阅here)。这意味着您可以完全并行运行32个任务。但即使这样也不能确保如果你启动32个线程,它们将同时完成。由于核心(某些缓存,所有RAM,硬盘,网卡等)之间共享资源的竞争,以及抢占式调度的本质上的随机性,可以估计线程所花费的时间量在一定范围内,但不完全是。

不幸的是,当你到达Ruby时,所有这一切都会崩溃。由于关于线程模型兼容性的一些哈利内部细节,只有一个线程可以执行ruby​​代码。所以,如果你的图像处理是用C语言完成的,欢乐喜悦。如果它是用Ruby编写的,那么世界上所有的脚步现在都不会帮你。

为了能够实际运行红宝石并行代码,您必须使用forkfork仅适用于Linux和Mac,而不适用于Windows,但您可以将其视为道路上的分叉。一个过程进入,两个过程出来。一次可以在所有不同的内核上运行多个进程。

因此,请采取@ Stefan的建议:使用一个队列和一些工作线程数=至#个CPU内核。并且不要期望你的电脑有那么多。现在你知道为什么;)。

+0

好吧,“假设你使用默认版本的Ruby,一次只能有一个线程执行ruby代码”。像JRuby或Rubinius这样的另一种Ruby实现将为您提供更好的线程特性,同时还可以安装这些东西。 – RyanWilcox

+0

@RyanWilcox:是。并且还处于不利的地位。 Icompatable库。尽管它的所有弱点,MRI仍然是事实上的标准。 – Linuxios

+0

那么你是说如果我的CPU支持例子2的线程,并且我每次启动2个线程,它将并行运行它们,但是如果我运行3000个线程将使系统按顺序运行它们? –