Определить нить, которая удерживает замок - PullRequest
32 голосов
/ 19 августа 2010

один из потоков в моем приложении заблокирован с помощью следующего оператора блокировки и привел к взаимоблокировке

void ExecuteCommand()
{
    lock(this._lockinstance)
    {
        // do some operation
    }
}

Можно ли легко определить, какой поток в данный момент удерживает блокировку? .. Мое приложение имеет более 50 потоков, что затрудняет прохождение каждого стека вызовов с помощью Visual Studio для определения потока, который удерживает блокировку

Ответы [ 6 ]

17 голосов
/ 19 августа 2010

Пример кода, который нужно попробовать:

class Test {
    private object locker = new object();
    public void Run() {
        lock (locker) {  // <== breakpoint here
            Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Установите точку останова на указанной строке.Когда он сломается, используйте Debug + Windows + Memory + Memory 1. Щелкните правой кнопкой мыши в окне и выберите «4-байтовое целое число».В поле Адрес введите & шкафчик.2-е слово - это идентификатор потока, которому принадлежит блокировка.Пройдите мимо оператора блокировки, чтобы увидеть его изменение.

Остерегайтесь того, что число - это идентификатор управляемого потока, а не идентификатор потока операционной системы, который вы видите в окне Отладка + Windows + Потоки.Это отстой, вы, вероятно, должны добавить в свою программу протоколирование, которое выводит значение ManagedThreadId, чтобы у вас был способ сопоставить значение с потоком.Обновление: исправлено в более поздних версиях VS, в окне отладчика Debug> Windows> Threads теперь отображается ManagedThreadId.

5 голосов
/ 01 сентября 2014

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

Старый путь:

private object myLock = new object();

...
lock(myLock)
{
    DoSomething();
}
...

С кодом ниже:

private SmartLock myLock = new SmartLock();

...
myLock.Lock( () =>
{
    DoSomething();
}
);
...

Источник:

public class SmartLock
{
    private object LockObject = new object();
    private string HoldingTrace = "";

    private static int WARN_TIMEOUT_MS = 5000; //5 secs


    public void Lock(Action action)
    {
        try
        {
            Enter();
            action.Invoke();
        }
        catch (Exception ex)
        {
            Globals.Error("SmartLock Lock action", ex);
        }
        finally
        {
            Exit();
        }

    }

    private void Enter()
    {
        try
        {
            bool locked = false;
            int timeoutMS = 0;
            while (!locked)
            {
                //keep trying to get the lock, and warn if not accessible after timeout
                locked = Monitor.TryEnter(LockObject, WARN_TIMEOUT_MS);
                if (!locked)
                {
                    timeoutMS += WARN_TIMEOUT_MS;
                    Globals.Warn("Lock held: " + (timeoutMS / 1000) + " secs by " + HoldingTrace + " requested by " + GetStackTrace());
                }
            }

            //save a stack trace for the code that is holding the lock
            HoldingTrace = GetStackTrace();
        }
        catch (Exception ex)
        {
            Globals.Error("SmartLock Enter", ex);
        }
    }

    private string GetStackTrace()
    {
        StackTrace trace = new StackTrace();
        string threadID = Thread.CurrentThread.Name ?? "";
        return "[" + threadID + "]" + trace.ToString().Replace('\n', '|').Replace("\r", "");
    }

    private void Exit()
    {
        try
        {
            Monitor.Exit(LockObject);
            HoldingTrace = "";
        }
        catch (Exception ex)
        {
            Globals.Error("SmartLock Exit", ex);
        }
    }
}
4 голосов
/ 19 августа 2010

Да, есть представление «Потоки», которое вы можете использовать в VS.Разрыв в любом месте вашего приложения (или нажмите кнопку «Разорвать все»), затем вы можете выбрать каждый поток и посмотреть, у кого есть блокировка (если есть).

Чтобы добавить его, перейдите в «Отладка»> «Windows»> «Потоки» (CTRL + D, Т)

3 голосов
/ 07 апреля 2016

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

Многие другие решения, опубликованные ранее, требуют написания нового класса, а затем преобразования всех блокировок (бла) в BetterLock (бла), что является большой работой для отладки и которую вы, возможно, не захотите в рабочей / поставленной версии ваш код. Другим требовалось подключить отладчик, который изменяет время кода и может скрыть проблему.

Вместо этого попробуйте следующее ...

Оригинальный код:

object obj = new object();
lock(obj)
{
    // Do stuff
}

Модифицированный код для отладки:

object _obj = new object();
object obj
{
    get
    {
        System.Diagnostics.StackFrame frame = new System.Diagnostics.StackFrame(1);
        System.Diagnostics.Trace.WriteLine(String.Format("Lock acquired by: {0} on thread {1}", frame.GetMethod().Name, System.Threading.Thread.CurrentThread.ManagedThreadId));
        return _obj;
    }
}
// Note that the code within lock(obj) and the lock itself remain unchanged.
lock(obj)
{
    // Do stuff
}

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

Нет, это не позволит вам определить, когда была снята блокировка, но если был выпущен своевременно, то у вас фактически не было проблемы конфликта блокировок.

3 голосов
/ 18 февраля 2016

Старые сообщения старые.

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

Я использую одноразовый класс для своего замка - мне нравится Monitor, но можно использовать любой механизм блокировки.

public class MonitorLock : IDisposable
{
    public static MonitorLock CreateLock(object value)
    {
        return new MonitorLock(value);
    }

    private readonly object _l;

    protected MonitorLock(object l)
    {
        _l = l;

        Console.WriteLine("Lock {0} attempt by {1}", _l, Thread.CurrentThread.ManagedThreadId);

        Monitor.Enter(_l);

        Console.WriteLine("Lock {0} held by {1}" , _l, Thread.CurrentThread.ManagedThreadId);
    }

    public void Dispose()
    {
        Monitor.Exit(_l);

        Console.WriteLine("Lock {0} released by {1}", _l, Thread.CurrentThread.ManagedThreadId);
    }
}

Я использую объект блокировки с именем, чтобы мне было ясно, какую блокировку я пытаюсь получить.

public class LockObject
{
    public string Name { get; set; }

    public LockObject(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        return Name;
    }
}

Наконец, создайте объект блокировки, а затем в блоке using удерживайте объект.

//create an object to lock on
private readonly object _requestLock = new LockObject("_requestLock");

using (MonitorLock.CreateLock(_requestLock))
{
    //do some work
}

Вывод должен быть чем-то вроде

Lock _requestLock attempt by 92
Lock _requestLock held by 92
Lock _requestLock attempt by 19
Lock _requestLock released by 92
Lock _requestLock held by 19
Lock _requestLock released by 19

Надеюсь, что кто-то найдет это полезным:)

1 голос
/ 19 августа 2010

Managed Stack Explorer из http://mse.codeplex.com/ или http://www.microsoft.com/downloadS/details.aspx?FamilyID=80cf81f7-d710-47e3-8b95-5a6555a230c2&displaylang=en отлично подходит в таких случаях.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...