当您不知道频道的实际数量时如何读取频道中的所有项目

时间:2019-09-10 12:15:20

标签: concurrency crystal-lang

我正在尝试实现一个访问某些URL,从中收集新的相对URL并生成报告的搜寻器。我正在尝试使用Crystal光纤和通道同时进行操作,如下所示:

urls = [...] # of String
visited_urls = []

pool_size.times do
  spawn do
    loop do
      url = urls.shift?
      break if url.nil?

      channel.send(url) if some_condition
    end
  end
end

# TODO: here the problem!
loop do
  url = channel.receive?
  break if url.nil? || channel.closed?

  visited_urls << url
end

puts visited_urls.inspect

但是这里我有一个问题-无限秒loop(它调用channel.receive?直到通道中的最后一个项目,然后等待一条永远不会到达的新消息)。之所以存在问题,是因为我永远不知道频道中实际有多少个项目,所以我不喜欢Crystal lang Guides的Concurency部分中的建议。

因此,当我们不知道该渠道将要存储多少物品并且需要接收多少物品时,也许有一些良好的做法如何使用该渠道?谢谢!

1 个答案:

答案 0 :(得分:5)

对此的一种常见解决方案是具有杀死值。作为主数据流的一部分,如下所示:

results = Channel(String|Symbol).new(POOL_SIZE * 2)

POOL_SIZE.times do
  spawn do
    while has_work?
      results.send "some work result"
    end

    results.send :done
  end
end

done_workers = 0

loop do
  message = results.receive
  if message == :done
    done_workers += 1
    break if done_workers == POOL_SIZE
  elsif message.is_a? String
    puts "Got: #{message}"
  end
end

或通过辅助渠道来通知事件:

results = Channel(String).new(POOL_SIZE * 2)
done = Channel(Nil).new(POOL_SIZE)

POOL_SIZE.times do
  spawn do
    while has_work?
      results.send "some work result"
    end

    done.send nil
  end
end

done_workers = 0
loop do
  select
  when message = results.receive
    puts "Got: #{message}"
  when done.receive
    done_workers += 1
    break if done_workers == POOL_SIZE
  end
end