我正在构建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?那么多线程有什么意义呢?
我想要的是:
同时运行所有线程而不是一个接一个地运行。所以在数学上,它将在完成1帧的同时完成所有1000帧,对吗?
还有人可以解释一下.join的作用吗? 从我的理解.join将停止主线程,直到你的线程完成或完成? 如果你不使用.join,那么线程将运行后台,主线程将继续。
那么使用.join有什么意义呢?我希望我的主线程继续运行并让后台的其他线程做什么?
感谢您的帮助/澄清!!
答案 0 :(得分:3)
只有拥有1000个CPU核心和大量(读取:数百和数百个)RAM才能实现。
连接点不是启动线程,而是等待线程完成。因此,在线程数组上调用join是等待所有线程完成的常见模式。
解释所有这些,并澄清你的误解,这需要深入挖掘。在C / Assembler级别,现代操作系统(Win,Mac,Linux和其他一些操作系统)使用抢先式调度程序。如果你只有一个核心,那么两个以paralel运行的程序就是一个完整的错觉。实际上,内核每隔几毫秒就会在两者之间进行切换,这使得所有人都可以慢慢地处理人类并行处理的错觉。
在更新,更现代的CPU中,通常有多个核心。今天最强大的CPU可以达到(我认为)16个真核+ 16个超线程核(参见here)。这意味着您实际上可以完全并行运行32个任务 。但即使这样也无法确保如果你启动32个线程,它们将同时完成。
由于对核心(某些缓存,所有RAM,硬盘,网卡等)之间共享的资源的竞争以及抢占式调度的基本随机性质,因此可以估计线程所花费的时间量。一定的范围,但不完全是。
不幸的是,当你到达Ruby时,所有这些都会崩溃。由于有关线程模型兼容性的一些内部细节,一次只能有一个线程执行 ruby 代码。所以,如果您的图像处理是在C中完成的,那就是快乐的快乐。如果它是用Ruby编写的,那么现在世界上所有的步骤都不会帮助你。
为了能够实际并行运行 Ruby 代码,您必须使用fork
。 fork
仅适用于Linux和Mac,而不适用于Windows,但您可以将其视为道路上的分叉。一个过程进入,两个过程出来。多个进程可以同时在所有不同的核心上运行。
所以,请听@ Stefan的建议:使用一个队列和一些工作线程=来自CPU核心数。并且不要指望你的电脑如此之多。现在你知道为什么了;)。
答案 1 :(得分:0)
所以
join
将接受所有线程并一个接一个地启动它们?
不,在调用Thread#new
时会启动线程。它创建一个新线程并在该线程中执行给定的块。
join
将停止主线程,直到您的线程完成或已完成?
是的,它会暂停执行,直到接收者(你的每个线程)都存在。
那么使用
join
是什么意思?
有时你想并行开始一些任务,但你必须等待每个任务完成才能继续。
我希望我的主线程继续运行并让后台其他线程做东西
然后不要致电join
。
毕竟,并行启动1,000个线程并不是一个好主意。您的计算机只能在CPU可用时并行运行任意数量的任务。因此,不是启动1,000个线程,而是将您的作业/任务放在队列/池中,并使用一些工作线程处理它们(CPU数量=工作者数量)。