面试题答案
一键面试并发工具选择
- 线程(Threads):适用于I/O密集型任务,在Ruby中线程是轻量级的,共享内存空间。但由于Ruby的全局解释器锁(GIL),在CPU密集型任务中并不能充分利用多核CPU。对于图片处理,调整大小可能涉及一些I/O操作(读取和写入文件),线程可在一定程度上提高效率。
- 进程(Processes):每个进程有独立的内存空间,可充分利用多核CPU,适合CPU密集型任务。在处理图片时,如果调整大小操作是CPU密集的(例如复杂的图像算法),进程是更好选择。但进程间通信和资源管理比线程复杂,开销也更大。
- 并发库:如
concurrent-ruby
库,它提供了更高级的并发抽象,如线程池、信号量等,简化并发编程,并且对Ruby的GIL有更好的处理。
核心代码示例 - 使用线程
require 'thread'
require 'chunky_png' # 用于图片处理的库
# 线程池大小
pool_size = 5
thread_pool = SizedQueue.new(pool_size)
# 图片文件路径数组
image_files = Dir['*.png']
# 调整图片大小的方法
def resize_image(file_path)
begin
img = ChunkyPNG::Image.from_file(file_path)
new_img = img.resize(200, 200) # 假设调整为200x200
new_img.save("resized_#{File.basename(file_path)}")
puts "#{file_path} 处理完成"
rescue StandardError => e
puts "处理 #{file_path} 时出错: #{e.message}"
end
end
# 创建并启动线程
image_files.each do |file|
thread_pool << Thread.new do
resize_image(file)
end
end
# 等待所有线程完成
thread_pool.each(&:join)
核心代码示例 - 使用进程
require 'fileutils'
require 'chunky_png'
# 图片文件路径数组
image_files = Dir['*.png']
# 调整图片大小的方法
def resize_image(file_path)
begin
img = ChunkyPNG::Image.from_file(file_path)
new_img = img.resize(200, 200) # 假设调整为200x200
new_img.save("resized_#{File.basename(file_path)}")
puts "#{file_path} 处理完成"
rescue StandardError => e
puts "处理 #{file_path} 时出错: #{e.message}"
end
end
# 创建并启动进程
image_files.each do |file|
pid = Process.fork do
resize_image(file)
exit
end
end
# 等待所有进程完成
Process.waitall
资源管理和错误处理
- 资源管理:
- 线程:共享内存可能导致资源竞争,可使用互斥锁(Mutex)来保护共享资源。在上述线程示例中,由于每个线程独立处理图片文件,不存在共享资源竞争问题。但如果存在共享资源(如共享日志文件),可这样使用互斥锁:
mutex = Mutex.new
def resize_image(file_path, mutex)
mutex.synchronize do
# 处理图片逻辑
end
end
- **进程**:每个进程有独立内存空间,无需像线程那样处理共享内存问题。但进程间通信(IPC)需使用管道(Pipe)、套接字(Socket)或共享内存等方式。在上述进程示例中,每个进程独立处理图片,不需要复杂的IPC。
2. 错误处理:
- 在resize_image
方法中,使用rescue StandardError
捕获可能出现的错误,无论是线程还是进程方式。这样能确保一个任务出错不会影响其他任务的执行。如果需要更细粒度的错误处理,可根据具体错误类型分别处理。例如,如果ChunkyPNG::Image.from_file
可能抛出文件不存在错误,可单独捕获:
def resize_image(file_path)
begin
img = ChunkyPNG::Image.from_file(file_path)
rescue Errno::ENOENT => e
puts "文件 #{file_path} 不存在: #{e.message}"
rescue StandardError => e
puts "处理 #{file_path} 时出错: #{e.message}"
end
# 后续处理逻辑
end