Ruby中Thread.new内loop块的作用及多线程相关疑问
关于Ruby多线程Queue与loop用法的疑问
我是多线程开发的新手,正在学习Ruby中Thread的工作原理,看到了一段示例代码:
require 'thread' queue = Queue.new producer = Thread.new do 10.times do queue.push(Time.now.to_i) sleep 1 end end consumers = [] 3.times do consumers << Thread.new do loop do unix_timestamp = queue.pop formatted_timestamp = unix_timestamp.to_s.reverse. gsub(/(\d\d\d)/, '\1,').reverse puts "It's been #{formatted_timestamp} seconds since the epoch!" end end end producer.join
我不理解最后一个Thread.new内的loop do的作用。运行上述代码时,会输出10条结果;若移除loop,则仅输出3条结果。我知道Ruby的Queue是线程安全的数据结构(类似用mutex.synchronize包裹数组),且queue.pop会等待队列有数据才执行。
我有两个问题:
- 针对该示例:为何用loop块包裹后,队列总共会被弹出10次?
- 通用问题:在多线程环境中,Thread.new内使用loop是否是惯例?我在多个教程和问答中看到该用法,若为惯例,原因是什么?
问题1解答:为何loop包裹后队列能被弹出10次?
- 无loop的情况:每个消费者线程仅执行一次
queue.pop,3个线程会弹出3个元素,之后线程直接结束。剩余7个元素留在队列中,但没有活跃线程处理,所以只输出3条结果。 - 有loop的情况:每个消费者线程会持续循环执行
queue.pop——处理完一个元素后,立刻回到循环开头,再次调用queue.pop等待下一个元素。 - 生产者总共推送10个元素,3个消费者线程会不断争抢队列中的元素,直到所有10个元素都被弹出处理,因此最终输出10条结果。(注:
producer.join仅等待生产者线程结束,但消费者线程会卡在queue.pop等待新元素,最终会随主线程结束而终止)
问题2解答:Thread.new内用loop是否是惯例?
这是多线程场景里的常见惯例,核心原因有两点:
- 适配持续任务处理需求:在生产者-消费者这类模型中,消费者线程需要长期监听队列,处理动态生成的任务。不用loop的话,线程处理完一个任务就退出,无法复用,频繁创建销毁线程会带来额外性能开销。
- 配合阻塞方法的特性:像
Queue#pop这类方法在队列空时会阻塞线程,不会占用CPU资源。用loop包裹后,线程会在有任务时自动唤醒处理,无任务时休眠,完美适配后台服务类线程的工作模式。 - 当然这不是绝对规则:如果线程只需要处理固定数量的任务,就不需要loop。但大部分多线程场景都需要线程长期存活处理动态任务,因此loop的用法十分普遍。
内容的提问来源于stack exchange,提问作者Iggy
相关产品推荐
相关产品推荐

