学习Ruby线程-线程完成时触发事件

学习Ruby线程-线程完成时触发事件,ruby,multithreading,concurrency,threadpool,Ruby,Multithreading,Concurrency,Threadpool,我是多线程新手,我正在寻找一些帮助,以了解线程完成后执行操作的惯用方法,例如更新进度条。在下面的示例中,我有几个项目和例程列表,用于对每个项目进行“解析”。我计划为每个列表设置一个进度条,以便能够让每个列表的解析例程更新已完成项目的百分比。我看到的唯一“触发器”点是项的sleepy方法(正在线程化的方法)末尾的puts语句。捕获完成的普遍接受的策略是什么,特别是当操作的范围在线程中运行的方法之外时 谢谢 # frozen_string_literal: true require 'concur

我是多线程新手,我正在寻找一些帮助,以了解线程完成后执行操作的惯用方法,例如更新进度条。在下面的示例中,我有几个项目和例程列表,用于对每个项目进行“解析”。我计划为每个列表设置一个进度条,以便能够让每个列表的解析例程更新已完成项目的百分比。我看到的唯一“触发器”点是项的sleepy方法(正在线程化的方法)末尾的puts语句。捕获完成的普遍接受的策略是什么,特别是当操作的范围在线程中运行的方法之外时

谢谢

# frozen_string_literal: true

require 'concurrent'

$stdout.sync = true

class TheList
  attr_reader :items

  def initialize(list_id, n_items)
    @id = list_id
    @items = []
    n_items.times { |n| @items << Item.new(@id, n) }
  end

  def parse_list(pool)
    @items.each do |item|
      pool.post { item.sleepy(rand(3..8)) }
    end
  end
end

class Item
  attr_reader :id

  def initialize (list_id, item_id)
    @id = item_id
    @list_id = list_id
  end

  def sleepy(seconds)
    sleep(seconds)
    # This puts statement signifies the end of the method threaded
    puts "List ID: #{@list_id} item ID:#{@id} slept for #{seconds} seconds"
  end
end

lists = []
5.times do |i|
  lists << TheList.new(i, rand(5..10))
end

pool = Concurrent::FixedThreadPool.new(Concurrent.processor_count)

lists.each do |list|
  list.parse_list(pool)
end
pool.shutdown
pool.wait_for_termination
#冻结\u字符串\u文字:true
需要“并发”
$stdout.sync=true
班级名单
属性读取器:项目
def初始化(列出\u id,n \u项)
@id=列表\u id
@项目=[]

n|items.times{n|@items问题不是真正的“知道线程何时完成”,而是如何在没有竞争条件的情况下更新共享进度条

解释问题:假设您有一个中心
ThreadList#progress\u var
变量,作为每个线程的最后一行,您使用
+=
将其递增。这将引入竞争条件,因为两个线程可以同时执行操作(并且可以覆盖彼此的结果)

为了解决这个问题,典型的方法是使用一个基本概念,如果您正在学习多线程,这是一个基本概念

实际实施并没有那么困难:

require 'mutex'

class ThreadList
  def initialize
    @semaphore = Mutex.new   
    @progress_bar = 0
  end
  def increment_progress_bar(amount)
    @semaphore.synchronize do
      @progress_bar += amount
    end
  end 
end

由于使用了
@semaphore.synchronize
块,您现在可以从线程中安全地调用这个
increment\u progress\u bar
方法,而不存在争用条件的风险。

谢谢@max pleaner!这让我在理解上有了很大的飞跃。我在列表类的初始化中添加了一个计数器和
@semaphore=Mutex.new
呃,我不只是向线程池中添加sleepy,而是增加了synchronize中的计数器。非常简单。现在我只是想弄清楚如何从外部监控线程池,但这是一个完全不同的问题。