Rubymultithreading性能问题

我正在构建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。 所以join会接受所有线程并一个接一个地启动它们? 这是否意味着它会等到线程1完成然后启动线程2? 那么multithreading有什么意义呢?

我想要的是这个:

同时运行所有线程而不是一个接一个地运行。 所以在数学上,它将在完成1帧的同时完成所有1000帧,对吧?

还有人能解释一下.join的作用吗? 从我的理解.join将停止主线程,直到您的线程已经或已完成? 如果你不使用.join,那么线程将运行后台,主线程将继续。

那么使用.join有什么意义呢? 我希望我的主线程继续运行并让后台的其他线程做东西?

感谢您的帮助/澄清!!

所以join会接受所有线程并一个接一个地启动它们?

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

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

是的,它将暂停执行,直到接收者(每个线程)存在。

那么使用join什么意义呢?

有时您希望并行启动某些任务,但必须等待每个任务完成才能继续。

我希望我的主线程继续运行,并让后台的其他线程做的事情

然后不要打电话join

毕竟,并行启动1,000个线程并不是一个好主意。 您的计算机只能在CPU可用时并行运行任意数量的任务。 因此,不是启动1,000个线程,而是将您的作业/任务放在队列/池中,并使用一些工作线程处理它们(CPU数量=工作者数量)。

只有拥有1000个CPU核心和大量(读取:数百和数百个)RAM才能实现这一点。

连接点不是启动线程,而是等待线程完成。 因此,在线程数组上调用join是等待所有线程完成的常见模式。

解释所有这些,并澄清你的误解,这需要深入挖掘。 在C / Assembler级别,现代操作系统(Win,Mac,Linux和其他一些操作系统)使用抢先式调度程序。 如果你只有一个核心,那么两个以paralel运行的程序就是一个完整的错觉。 实际上,内核每隔几毫秒就在两者之间切换,这使得所有人都可以慢慢地处理人类并行处理的错觉。

在更新,更现代的CPU中,通常有多个核心。 今天最强大的CPU可以达到(我认为)16个真核+ 16个超线程核(见这里 )。 这意味着您可以实际完全并行运行32个任务。 但即使这样也无法确保如果你启动32个线程,它们将同时完成。

由于对核心(一些缓存,所有RAM,硬盘,网卡等)之间共享的资源的竞争,以及抢占式调度的基本随机性,可以在一定范围内估计线程所花费的时间量。 ,但不完全是。

不幸的是,当你到达Ruby时,所有这些都会崩溃。 由于一些关于线程模型兼容性的内部细节, 只有一个线程可以一次执行ruby代码 。 所以,如果您的图像处理是在C中完成的,那就是快乐的快乐。 如果它是用Ruby编写的,那么,世界上所有的步骤都不会对你有所帮助。

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

因此,请参考@Stefan的建议:使用队列和一些工作线程=来自CPU核心数。 并且不要指望你的电脑如此之多。 现在你知道为什么了;)。