调试线程 ruby 应用程序
Debugging threaded ruby applications
我正在开发一个使用线程的 ruby 应用程序。我很难调试它。主要问题是我想单步执行一个特定的线程。我设置了断点和 运行,但在我使用 thr sw 2
之类的东西之前没有任何反应。但是随后的输入是 FUBAR'd。我试过 pry,但是 pry 似乎并不擅长处理线程。
解决方案?解决方法?
编辑 1:
仅供参考:Pry version 0.10.1 on Ruby 2.0.0
和 byebug
这就是我使用 Pry 的经历。尝试 1:I 使用 break
设置线程特定的断点
- 让程序运行。
- 触发断点。
- Pry 正确地显示了断点和周围的代码。
- 现在什么都没有了。 我没有提示。 Control-C 不起作用。事实上,我必须从外部 kill -9 进程。
尝试2:使用上面的"binding.pry"方法。
- 一样
- 一样
- 一样!
- 我收到一个 Pry 提示!我可以产生表达式。
- 我尝试使用 "step" 或 "next",突然我进入了 "Pry::history#load"。所以现在调试器已经跳转到处理输入本身的线程。这不是功能性的。
撬输出:
[2] pry(#<QDS::Node::Primary>)> step
From: /usr/local/rvm/gems/ruby-2.0.0-p598/gems/pry-0.10.1/lib/pry/history.rb @ line 37 Pry::History#load:
35: def load
36: @loader.call do |line|
=> 37: @pusher.call(line.chomp)
38: @history << line.chomp
39: @original_lines += 1
40: end
41: end
[2] pry(#<Pry::History>)>
- 我尝试 "exit",现在我在 "pry(main)" 没有任何其他工作。
撬输出:
[1] pry(main)> continue
Error: Cannot find local context. Did you use `binding.pry`?
pry
可以很好地处理线程。您的代码中可能存在故障。
让我们尝试检查以下线程切换器(示例取自 here):
require 'pry'
module SeqExec
class Seqs
attr_reader :init
def synch_prior mx, cv
Thread.new {
mx.synchronize {
@init[:prior] = true
loop do
cv.wait mx
# binding.pry
yield if block_given?
cv.broadcast
end
}
}
end
def synch_posterior mx, cv
Thread.new {
mx.synchronize {
@init[:posterior] = true
loop do
cv.wait mx
yield if block_given?
cv.broadcast
end
}
}
end
def synch λ1, λ2
@init = {}
mx = Mutex.new
cv = ConditionVariable.new
synch_prior(mx, cv, &λ1) # prior function
Thread.pass until {:prior=>true} == @init
synch_posterior(mx, cv, &λ2) # posterior function
Thread.pass until {:prior=>true,:posterior=>true} == @init
cv.signal # we are ready to start
end
end
end
module SeqExec
Thread.abort_on_exception = true
def pre &cb
@prior = cb
end
def post &cb
@posterior = cb
end
def run λ1 = nil, λ2 = nil
pre &λ1 if λ1
post &λ2 if λ2
raise ArgumentError.new "Cannot run sequential execution, lambdas are not set" \
unless (@prior && @posterior)
Seqs.new.synch @prior, @posterior
end
end
include SeqExec
@i=0
@stack = []
pre { sleep 0.3; print "-#{@i += 1}-"; @stack.push(@i) }
post { print "|#{@stack.pop}|" }
run
10.times { sleep 0.1 }
sleep 30000
注释掉 binding.pry
后,它会打印:
#⇒ -1-|1|-2-|2|-3-|3|-4-|4|-5-|5|-6-|6|-7-........
取消注释 binding.pry
,我们得到:
Frame number: 0/2
From: /tmp/a.rb @ line 12 SeqExec::Seqs#synch_prior:
6: def synch_prior mx, cv
7: Thread.new {
8: mx.synchronize {
9: @init[:prior] = true
10: loop do
11: cv.wait mx
=> 12: binding.pry
13: yield if block_given?
14: cv.broadcast
15: end
16: }
17: }
18: end
▶ mx
=> #<Mutex:0xb21f204>
▶ exit
-1-|1|
Frame number: 0/2
From: /tmp/a.rb @ line 12 SeqExec::Seqs#synch_prior:
6: def synch_prior mx, cv
7: Thread.new {
8: mx.synchronize {
9: @init[:prior] = true
10: loop do
11: cv.wait mx
=> 12: binding.pry
13: yield if block_given?
14: cv.broadcast
15: end
16: }
17: }
18: end
▶ exit
-2-|2|
Frame number: 0/2
...
不用说,上面的意思是线程停止,直到pry
恢复。
我正在开发一个使用线程的 ruby 应用程序。我很难调试它。主要问题是我想单步执行一个特定的线程。我设置了断点和 运行,但在我使用 thr sw 2
之类的东西之前没有任何反应。但是随后的输入是 FUBAR'd。我试过 pry,但是 pry 似乎并不擅长处理线程。
解决方案?解决方法?
编辑 1:
仅供参考:Pry version 0.10.1 on Ruby 2.0.0
和 byebug
这就是我使用 Pry 的经历。尝试 1:I 使用 break
- 让程序运行。
- 触发断点。
- Pry 正确地显示了断点和周围的代码。
- 现在什么都没有了。 我没有提示。 Control-C 不起作用。事实上,我必须从外部 kill -9 进程。
尝试2:使用上面的"binding.pry"方法。
- 一样
- 一样
- 一样!
- 我收到一个 Pry 提示!我可以产生表达式。
- 我尝试使用 "step" 或 "next",突然我进入了 "Pry::history#load"。所以现在调试器已经跳转到处理输入本身的线程。这不是功能性的。
撬输出:
[2] pry(#<QDS::Node::Primary>)> step
From: /usr/local/rvm/gems/ruby-2.0.0-p598/gems/pry-0.10.1/lib/pry/history.rb @ line 37 Pry::History#load:
35: def load
36: @loader.call do |line|
=> 37: @pusher.call(line.chomp)
38: @history << line.chomp
39: @original_lines += 1
40: end
41: end
[2] pry(#<Pry::History>)>
- 我尝试 "exit",现在我在 "pry(main)" 没有任何其他工作。
撬输出:
[1] pry(main)> continue
Error: Cannot find local context. Did you use `binding.pry`?
pry
可以很好地处理线程。您的代码中可能存在故障。
让我们尝试检查以下线程切换器(示例取自 here):
require 'pry'
module SeqExec
class Seqs
attr_reader :init
def synch_prior mx, cv
Thread.new {
mx.synchronize {
@init[:prior] = true
loop do
cv.wait mx
# binding.pry
yield if block_given?
cv.broadcast
end
}
}
end
def synch_posterior mx, cv
Thread.new {
mx.synchronize {
@init[:posterior] = true
loop do
cv.wait mx
yield if block_given?
cv.broadcast
end
}
}
end
def synch λ1, λ2
@init = {}
mx = Mutex.new
cv = ConditionVariable.new
synch_prior(mx, cv, &λ1) # prior function
Thread.pass until {:prior=>true} == @init
synch_posterior(mx, cv, &λ2) # posterior function
Thread.pass until {:prior=>true,:posterior=>true} == @init
cv.signal # we are ready to start
end
end
end
module SeqExec
Thread.abort_on_exception = true
def pre &cb
@prior = cb
end
def post &cb
@posterior = cb
end
def run λ1 = nil, λ2 = nil
pre &λ1 if λ1
post &λ2 if λ2
raise ArgumentError.new "Cannot run sequential execution, lambdas are not set" \
unless (@prior && @posterior)
Seqs.new.synch @prior, @posterior
end
end
include SeqExec
@i=0
@stack = []
pre { sleep 0.3; print "-#{@i += 1}-"; @stack.push(@i) }
post { print "|#{@stack.pop}|" }
run
10.times { sleep 0.1 }
sleep 30000
注释掉 binding.pry
后,它会打印:
#⇒ -1-|1|-2-|2|-3-|3|-4-|4|-5-|5|-6-|6|-7-........
取消注释 binding.pry
,我们得到:
Frame number: 0/2
From: /tmp/a.rb @ line 12 SeqExec::Seqs#synch_prior:
6: def synch_prior mx, cv
7: Thread.new {
8: mx.synchronize {
9: @init[:prior] = true
10: loop do
11: cv.wait mx
=> 12: binding.pry
13: yield if block_given?
14: cv.broadcast
15: end
16: }
17: }
18: end
▶ mx
=> #<Mutex:0xb21f204>
▶ exit
-1-|1|
Frame number: 0/2
From: /tmp/a.rb @ line 12 SeqExec::Seqs#synch_prior:
6: def synch_prior mx, cv
7: Thread.new {
8: mx.synchronize {
9: @init[:prior] = true
10: loop do
11: cv.wait mx
=> 12: binding.pry
13: yield if block_given?
14: cv.broadcast
15: end
16: }
17: }
18: end
▶ exit
-2-|2|
Frame number: 0/2
...
不用说,上面的意思是线程停止,直到pry
恢复。