Вызывающий / обратный след за потоком

Насколько мне известно, можно получить только ту часть информации о вызывающем абоненте / обратной трассировке, которая находится в текущем потоке; все, что до этого (в потоке, создавшем текущий поток), обрезается. Следующее иллюстрирует это; тот факт, что a вызвал b, который вызвал c, создавший поток, который вызвал d, отключен:

def a; b end
def b; c end
def c; Thread.new{d}.join end
def d; e end
def e; puts caller end

a
# => this_file:4:in `d'
#    this_file:3:in `block in c'
  1. В чем причина этой особенности?
  2. Есть ли способ получить информацию о вызывающем абоненте / обратной трассировке за пределами текущего потока?

person sawa    schedule 23.05.2014    source источник


Ответы (2)


Думаю, я пришел к своему ответу.

То, что можно сделать с потоком извне, - это не только его создание. Кроме создания, вы можете сделать пробуждение и т. Д. Поэтому неясно, какую операцию следует отнести к вызывающей стороне. Например, предположим, что есть поток:

1: t = Thread.new{
2:   Thread.stop
3:   puts caller
4: }
5: t.wakeup

Поток t создается в строке 1, но он сам переходит в спящий режим в строке 2, затем он просыпается в строке 5. Итак, когда мы находимся в строке 3 caller и рассматриваем вызывающую часть вне потока, она неясно, должен ли Thread.new в строке 1 быть его частью, или t.wakeup в строке 5 должен быть его частью. Следовательно, нет четкого определения вызывающих абонентов за пределами текущего потока.

Однако, если мы определим четкое понятие, то для вызывающей стороны, находящейся за пределами потока, возможно, будет смысл. Например, может иметь смысл всегда добавлять вызывающих абонентов до создания потока. В противном случае может иметь смысл добавление вызывающих, ведущих к самому последнему wakeup или созданию. Это зависит от определения.

person sawa    schedule 23.05.2014

Ответ на оба ваших вопроса действительно одинаковый. Рассмотрим немного более сложный основной поток. Вместо того, чтобы просто ждать, пока порожденный поток завершится в c, основной поток продолжает вызывать другие функции, возможно, даже возвращается из c и занимается своими делами, в то время как порожденный поток продолжает свои дела.

Это означает, что стек в основном потоке изменился с момента создания потока, начинающегося в d. Другими словами, к тому моменту, когда вы вызываете puts caller, стек в основном потоке уже не находится в том состоянии, в котором он был при создании вторичного потока. Невозможно безопасно вернуться вверх по стеку дальше этой точки.

Вкратце:

  1. Стек порождающего потока не останется в том состоянии, в котором он был, когда поток был порожден, поэтому возвращение за пределы начала собственного стека потока небезопасно.

  2. Нет, поскольку вся идея потоков заключается в том, что они (псевдо) параллельны, их стеки совершенно не связаны.

Обновление:

Как предлагается в комментариях, стек текущего потока можно скопировать в новый поток во время создания. Это позволит сохранить информацию, ведущую к создаваемому потоку, но решение не лишено собственного набора проблем.

  1. Создание потока будет медленнее. Это могло быть нормально, если бы от этого была какая-то выгода, но в данном случае так ли?

  2. Что будет означать возврат из функции входа в поток?

    • It could return to the function that created the thread and keep running as if it was just a function call - only that it now runs in the second thread, not the original one. Would we want that?
    • Может быть какая-то магия, которая гарантирует, что поток завершится, даже если он не находится на вершине стека вызовов. Это в любом случае сделало бы информацию в стеке вызовов над функцией входа в поток некорректной.
  3. В системах с ограничениями на размер стека для каждого потока вы можете столкнуться с проблемами, когда поток исчерпает стек, даже если он сам по себе не очень много использует.

Вероятно, есть и другие сценарии и особенности, которые можно было бы продумать, но способ создания потоков с их собственным пустым стеком для начала делает модель простой и предсказуемой, не оставляя никакой полезной информации из стека вызовов.

person harald    schedule 23.05.2014
comment
Ваш ответ объясняет, почему текущая реализация не может вернуть информацию. Но я не вижу в этом принципиального невозможного. Если каждый поток копирует в момент своего создания информацию о вызывающем абоненте до того момента, когда он был создан, и сохраняет ее внутри себя, не будет ли возможно получить всю информацию из этого потока? Я ошибся? Я чувствую, что могу реализовать это даже на уровне Ruby. - person sawa; 23.05.2014
comment
Нет ничего невозможного, но есть ли выгода? См обновленный ответ. - person harald; 23.05.2014