Подтвердить что ты не робот

Определите поток, который содержит блокировку

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

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

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

4b9b3361

Ответ 1

Пример кода для тестирования:

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-байтное целое число". В поле Адрес введите & locker. Второе слово - это идентификатор потока нити, которому принадлежит блокировка. Пройдите мимо инструкции блокировки, чтобы увидеть ее изменение.

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

Ответ 2

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

Старый способ:

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);
        }
    }
}

Ответ 3

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

Чтобы добавить его, перейдите в Debug > Windows > Темы (Ctrl + D, T)

Ответ 4

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

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

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

Исходный код:

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 для последней записи. Конечно, вы можете выводить любую другую информацию, которая может оказаться полезной и в геттере.

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

Ответ 5

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

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

Ответ 6

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

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

Я использую одноразовый класс для моей блокировки - мне нравится 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;
    }
}

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

//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

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