在 Crystal 语言中杀死一个线程
Killing a Thread in Crystal lang
我编写了一个 crystal 程序来计算 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) 只是 returns 一个数组。然后计算并打印数组大小。您可以看到动画冻结了一段时间,然后继续,直到它被打印并退出。如果我使用 spawn do...end
spawn 中的打印会暂停,直到计算出筛分。
不杀死线程会导致这样的问题
在ruby我曾经做过
t = Thread.new { loop while ... }
<some other time consuming stuff here>
t.kill
return calculated_stuffs
Crystal详情
Crystal 0.31.1 (2019-10-21)
LLVM:9.0.0
默认目标:x86_64-pc-linux-gnu
如何终止 crystal 中的线程?
Thread
是 Crystal 内部 API 的一部分,不能直接使用。
好消息是 Crystal 原生支持称为 CSP 的并发模型,其中 Fibers(轻量级线程)通过线程安全通道相互发送消息以进行协调。因此,Fiber 不是通过共享状态进行通信,而是通过通信来共享状态 - 正如他们在 golang
.
中所说的那样
对于您的用例,您可以 运行 3 个光纤:
- 筛子,生成数字并通过通道发送更新
- 监视器,在筛子的通道上接收,更新 UI 并在筛子完成后发送完成消息
- 主纤程,等待监视器通知完成并能够决定如何处理筛选结果
您的代码可能如下所示
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
我编写了一个 crystal 程序来计算 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) 只是 returns 一个数组。然后计算并打印数组大小。您可以看到动画冻结了一段时间,然后继续,直到它被打印并退出。如果我使用 spawn do...end
spawn 中的打印会暂停,直到计算出筛分。
不杀死线程会导致这样的问题
在ruby我曾经做过
t = Thread.new { loop while ... }
<some other time consuming stuff here>
t.kill
return calculated_stuffs
Crystal详情
Crystal 0.31.1 (2019-10-21)
LLVM:9.0.0 默认目标:x86_64-pc-linux-gnu
如何终止 crystal 中的线程?
Thread
是 Crystal 内部 API 的一部分,不能直接使用。
好消息是 Crystal 原生支持称为 CSP 的并发模型,其中 Fibers(轻量级线程)通过线程安全通道相互发送消息以进行协调。因此,Fiber 不是通过共享状态进行通信,而是通过通信来共享状态 - 正如他们在 golang
.
对于您的用例,您可以 运行 3 个光纤:
- 筛子,生成数字并通过通道发送更新
- 监视器,在筛子的通道上接收,更新 UI 并在筛子完成后发送完成消息
- 主纤程,等待监视器通知完成并能够决定如何处理筛选结果
您的代码可能如下所示
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