如何让ruby pry停止所有其他线程

时间:2017-05-28 01:07:42

标签: ruby multithreading pry

我正在尝试调试多线程ruby脚本,问题出在我做的时候

binding.pry

其他线程继续将输出发送到控制台。如何让它们在binding.pry停止然后在我退出时再次启动?我想在.pryrc

中有办法做到这一点

3 个答案:

答案 0 :(得分:4)

听起来你提议使用binding.pry的调用来询问所有子线程并暂停它们直到你结束你的pry会话。出于技术和实际原因,这是不可能的。 BindingThread类不能以这种方式工作,Ruby中的多线程不会那样工作。

Ruby中的线程只能通过调用Kernel#sleepThread.stop暂停。 (这些在功能上是等价的)至关重要的是,这些方法只能在当前线程上调用One thread cannot suspend another thread。 (Thread.stop是一个类方法,而不是实例方法)

让我们看看binding.pry实际上做了什么:类Binding的对象将执行上下文封装在代码中的某个特定位置,并保留此上下文以供将来使用。因此,当您将binding.pry放入代码中时,您要告诉Ruby封装当前线程的执行上下文。

这意味着当您在主线程中调用binding.pry时,Binding对象具有当前线程的上下文并且可以告诉自己睡眠,但核心Ruby Thread类不会允许它告诉任何其他线程睡觉。

即使它确实支持它,它也会很奇怪且容易出错,并且引起很多人头疼。想象一下,你有这样的代码:

# we are in the main thread
Thread.new do
  # we are in the child thread
  foo = Foo.new(bar.fetch(:baz, {}))
  foo.save
end

# we are in the main thread
binding.pry

由于Ruby处理context-switching的方式,如果binding.pry告诉所有子线程停止,则子线程可能会在调用堆栈中的任何地方停止,包括Foo.new代码中的任何位置或.save。让这些线程暂停并在执行您未编写的代码的过程中恢复将导致您遇到麻烦。例如,如果检出来自池的ActiveRecord连接并将其用于SELECT查询但是线程在返回到池的连接之前以及在获得响应之前进入休眠状态会发生什么?坏的东西。很多坏事。

听起来真正的解决方案是改变子线程的详细程度。如果您正在对繁琐的代码进行故障排除,并且当您尝试在单个线程中工作时其他线程处于嘈杂状态,则将其他线程设置为使用,例如,暂时降低日志记录级别。

答案 1 :(得分:3)

如果binding.pry产生不一致的结果,请尝试以下操作:

  1. 如果存在,请从gemfile中删除pry-stack_explorer然后 重新包装您的申请。宝石似乎有问题 pry-byebug

  2. 此外,不确定您是否已经尝试过这个但是有几台服务器     重启不会伤害;这解决了这样的奇怪问题     我经常比我更愿意承认。

  3. 但是,您可能正在尝试使用binding.pry执行无法完成的操作:

    必须理解的关于binding.pry的一个基本原则是它的范围只包括当前线程,而不是你所描述的多线程应用程序中的每个线程。

    假设,如果binding.pry确实影响了每个线程(同样,它没有),这将导致不可预测的行为,特别是如果某些线程正在执行数据访问/检索/更新操作。

    要实现您的目标,您可能需要采取不同的方法:

    虽然根据应用程序中有多少个线程可能会很繁琐,但您可能需要单独控制/停止每个线程。显然,这可以通过Thread.stop完成。

答案 2 :(得分:2)

根据the FAQ

  

线程不起作用,出了什么问题?

     

某些系统(特别是Mac OS X)使用Editline而不是GNU Readline。当使用Editline库编译时,Ruby Readline库当前存在一个问题,它使得它阻塞所有线程而不仅仅是调用Readline.readline()的线程(它在阻止全局VM锁定时阻塞,阻止其他线程获取它)。这可以通过安装GNU Readline on OS X来解决。

听起来好像Rubys使用Editline构建了所有线程,并且Pry的开发人员认为这是一个bug。我找不到任何方法来使用Pry自动阻止从一次调用binding.pry的所有线程。

This answer建议在调用binding.pry之前使用Mutex同步您的线程。显然,这需要更改代码,您可能不希望仅为了调试而同步线程。另一方面,同步线程确实可以减少不确定性,并且可以在不使用Pry的情况下简化调试。

作为替代方案,您可以在每个线程中添加断点:

require 'pry'

Thread.new do
  10.times do |i|
    binding.pry
    puts "subthread: #{i}"
  end
end


10.times do |i|
  binding.pry
  puts "main thread: #{i}"
end

这将在特定时刻停止主线程和子线程的执行。不幸的是,如果你想检查另一个线程或确切地知道当前线程停止时的位置,这没有任何帮助。如果当前没有调试,那么注释掉所有断点也有点痛苦。 (但是您可以通过将DISABLE_PRY环境变量设置为非零值来完全禁用Pry。)

如果你真的需要调试多线程程序,你可能希望使用GDB之类的东西来提供更多的支持。