Como pedir a Ruby hacer palanca para detener todos los otros hilos.
multithreading pry (3)
Estoy tratando de depurar un script de Ruby multiproceso, el problema es cuando lo hago
binding.pry
Los otros hilos continúan enviando salida a la consola. ¿Cómo hago que se detengan en binding.pry y luego vuelvo a arrancar cuando salgo? Estoy pensando que hay una manera de hacer esto en .pryrc
Parece que está proponiendo usar la invocación de binding.pry
para interrogar todos los hilos secundarios y suspenderlos hasta que finalice su sesión de palanca. Eso no es posible por razones técnicas y prácticas. Las clases de Binding
y Thread
no funcionan de esa manera, y el multihilo en Ruby no funciona de esa manera.
Los hilos en Ruby solo se pueden suspender llamando a Kernel#sleep
o Thread.stop
. (y esos son funcionalmente equivalentes) Crucialmente, estos métodos solo pueden invocarse en el hilo actual . Un hilo no puede suspender otro hilo . ( Thread.stop
es un método de clase, no un método de instancia)
Veamos lo que realmente hace binding.pry
: los objetos de Class Binding encapsulan el contexto de ejecución en algún lugar particular del código y conservan este contexto para uso futuro. Así que cuando binding.pry
en tu código, le estás diciendo a Ruby que encapsule el contexto de ejecución para el hilo actual .
Lo que eso significa es que cuando se llama a binding.pry
en el hilo principal, el objeto Binding tiene contexto para el hilo actual y puede decirse que binding.pry
en reposo, pero la clase principal de Ruby Thread no le permite decirle a otros subprocesos que duerman.
Incluso si lo apoyara, sería extraño y propenso a errores y la causa de muchos rascarse la cabeza. Imagina que tienes un código como este:
# 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
Debido a la forma en que Ruby maneja context-switching , si binding.pry
ordenó a todos los subprocesos secundarios que se detuvieran, entonces el subproceso secundario podría detenerse EN CUALQUIER LUGAR en la pila de llamadas, incluso en cualquier parte del código para Foo.new
o .save
. Tener esos subprocesos en pausa y reanudarse en medio de la ejecución de un código que no ha escrito le causará problemas. Por ejemplo, ¿qué sucede si una conexión ActiveRecord del grupo se verifica y se usa para una consulta SELECT
pero el hilo se puso en suspensión antes de devolver la conexión al grupo y antes de que recibiera una respuesta? Cosas malas. Muchas cosas malas.
Parece que la solución real para usted es cambiar la verbosidad de los hilos secundarios. Si está solucionando problemas con el código de chat y sus otros subprocesos son ruidosos mientras intenta trabajar en un único subproceso, entonces configure los otros subprocesos para que utilicen, por ejemplo, un nivel de registro más bajo temporalmente.
Según las preguntas frecuentes :
Los hilos no funcionan, ¿qué pasa?
Algunos sistemas (Mac OS X en particular) usan Editline en lugar de GNU Readline. La biblioteca Ruby Readline actualmente tiene un problema cuando se compila con la biblioteca Editline, lo que hace que bloquee todos los subprocesos en lugar de solo el que llama a Readline.readline () (bloquea mientras mantiene el bloqueo de VM global, lo que evita que otros subprocesos lo adquieran). Esto se puede solucionar instalando GNU Readline en OS X.
Así que suena como si Rubys, construido con Editline, bloquee todos los subprocesos como espera y que los desarrolladores de Pry lo consideren un error. No pude encontrar ninguna forma de usar Pry para bloquear automáticamente todos los hilos de una llamada a binding.pry
.
Esta respuesta sugiere usar un Mutex para sincronizar sus hilos antes de llamar a binding.pry
. Obviamente, eso requiere cambios en su código y es posible que no desee sincronizar los hilos por el solo hecho de depurar. Por otro lado, la sincronización de subprocesos reduce la incertidumbre y puede facilitar la depuración sin utilizar Pry en absoluto.
Como alternativa, puedes poner puntos de ruptura en cada hilo:
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
Eso detendrá la ejecución de los subprocesos principal y secundario en un momento específico. Desafortunadamente, eso no ayuda si desea inspeccionar el otro hilo o saber exactamente dónde estaba cuando se detuvo el hilo actual. También es un poco molesto comentar todos los puntos de interrupción si actualmente no se está depurando. (Pero puede deshabilitar Pry por completo configurando la variable de entorno DISABLE_PRY en un valor no nulo).
Si realmente necesita depurar un programa multihilo, probablemente querrá usar algo como GDB que proporciona más soporte.
Si binding.pry está dando resultados inconsistentes, intente lo siguiente:
Si existe, elimine
pry-stack_explorer
de su gemfile y vuelva a empaquetar su aplicación. Esa gema parece tener problemas conpry-byebug
.Además, no estoy seguro si ya lo intentaste, pero algunos reinicios del servidor no pudieron hacer daño; Esto ha resuelto problemas extraños como este para mí más a menudo de lo que me gustaría admitir.
Sin embargo, puede estar intentando hacer algo que simplemente no se puede hacer con binding.pry:
Un binding.pry
fundamental de binding.pry
que debe entenderse es que su alcance incluiría solo el subproceso actual, no todos los subprocesos en una aplicación de subprocesos múltiples como ha descrito.
Hipotéticamente, si binding.pry
afectara a todos los subprocesos (de nuevo, no lo hace), esto daría como resultado un comportamiento impredecible, especialmente si algunos subprocesos están realizando operaciones de acceso / recuperación / actualización de datos.
Para lograr lo que desea, es posible que deba adoptar un enfoque diferente:
Aunque puede ser tedioso dependiendo de la cantidad de subprocesos en su aplicación, es posible que necesite controlar / detener cada subproceso individualmente. Obviamente, esto se puede hacer con Thread.stop
.