【问题标题】:Killing a Thread in Crystal lang在 Crystal lang 中杀死线程
【发布时间】:2019-11-15 12:27:24
【问题描述】:

我写了一个程序,一个水晶程序,用 Sieve 计算一个范围内的素数。

代码

#!/usr/bin/env crystal

def sieve(max)
    t = Thread.new do
        dot, ary, colours = ".", ["\xE2\xA0\x81", "\xE2\xA0\x88", "\xE2\xA0\xA0", "\xE2\xA0\x84"] * 2, [154, 184, 208, 203, 198, 164, 129, 92]
        print "\e[?25l"

        loop do
            ary.size.times do |x|
                print("\e[2K#{ary[x]} \e[38;5;#{colours[x]}mPlease Wait#{dot * x}\e[0m\r")
                sleep(0.1)
            end
        end
    end

    s = [nil, nil] + (2..max).to_a
    s.each do |x|
        next unless x
        break if (sq = x ** 2) > max
        (sq..max).step(x) { |y| s[y] = nil }
    end

    puts "\e[?25h"
    s.tap { |x| x.compact! }
end

p sieve(2_000_000).size

我想显示的方式是

问题

问题是当 puts 写筛子时线程没有被杀死。方法 sieve(n) 只返回一个数组。然后计算并打印数组大小。您可以看到动画冻结了一段时间,然后继续,直到它被打印并退出。如果我使用spawn do...end,则 spawn 中的打印会暂停,直到计算出筛子。

不杀死线程会导致这样的问题

我以前用红宝石做

t = Thread.new { loop while ... }
<some other time consuming stuff here>

t.kill
return calculated_stuffs

水晶细节

水晶 0.31.1 (2019-10-21)

LLVM:9.0.0 默认目标:x86_64-pc-linux-gnu


如何杀死水晶中的线程?

【问题讨论】:

  • 您可以添加一个布尔值“keep_running”或可以向该线程发送消息的通道,例如“停止运行”并每隔一段时间对其进行轮询,FWIW...

标签: multithreading crystal-lang


【解决方案1】:

Thread 是 Crystal 内部 API 的一部分,不能直接使用。

好消息是 Crystal 原生支持称为 CSP 的并发模型,其中 Fiber(轻量级线程)通过线程安全通道相互发送消息以进行协调。因此,Fiber 不是通过共享状态进行通信,而是通过通信来共享状态 - 正如他们在 golang 中所说的那样。

对于您的用例,您可以运行 3 个 Fiber:

  • 筛子,生成数字并通过通道发送更新
  • 监视器,在筛子的频道上接收,更新 UI 并在筛子完成后发送完成消息
  • 主 Fiber,等待监视器通知完成并能够决定如何处理筛子的结果

这是您的代码的样子

record Result, primes : Array(Int32)
record Tick
alias SieveUpdate = Result | Tick

def monitor(updates : Channel(SieveUpdate)) : Channel(Result)
  Channel(Result).new.tap { |done|
    spawn do
      dot, ary, colours = ".", ["\xE2\xA0\x81", "\xE2\xA0\x88", "\xE2\xA0\xA0", "\xE2\xA0\x84"] * 2, [154, 184, 208, 203, 198, 164, 129, 92]
      ary_idx = 0
      update_n = 0
      print "\e[?25l"
      loop do
        case value = updates.receive
        when Tick
          next unless (update_n+=1) % 50 == 0 # lower refresh rate
          print("\e[2K#{ary[ary_idx]} \e[38;5;#{colours[ary_idx]}mPlease Wait#{dot * ary_idx}\e[0m\r")
          ary_idx = (ary_idx + 1) % ary.size
        when Result
          puts "\e[?25h"
          done.send value
          break
        end
      end
    end
  }
end

def sieve(max) : Channel(SieveUpdate)
  Channel(SieveUpdate).new.tap { |updates|
    spawn do
      s = [nil, nil] + (2..max).to_a
      s.each do |x|
          updates.send(Tick.new)
          next unless x
          break if (sq = x ** 2) > max
          (sq..max).step(x) { |y| s[y] = nil }
      end

      updates.send Result.new(s.compact.as(Array(Int32)))
    end
  }
end

updates = sieve(2_000_000)
done = monitor(updates)

print done.receive.primes.size

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2010-11-09
    • 2014-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多