Метод взаимоблокировки lock ()

Я столкнулся с тупиком, моя структура кода похожа на это:

private delegate void UpdateControlDelegate(string value);

public void UpdateControl(string value)
{
    if (txtAddress.InvokeRequired)
    {
        txtAddress.Invoke(new UpdateControlDelegate(UpdateControl), value);
    }
    else
    {
        txtAddress.Text = value; // This is in GroupBox1
        txtValue.Text = value; // This is in GroupBox2
    }
}

class ThreadHandler
{
    List<string> _list = new List<string>();
    object _criticalSection = new object();

    public ThreadHandler()
    {
        new Thread(new ThreadStart(Run)).Start();
    }

    public static ThreadHandler _threadHandler = null;

    public static ThreadHandler GetThreadHandler()
    {
        if (_threadHandler == null)
        {
            _threadHandler = new ThreadHandler();
        }

        return _threadHandler;
    }

    public void Run()
    {
        while (true)
        {
            // some code

            lock (_criticalSection)
            {
                foreach (string str in _list)
                {
                    // some Code
                }
            }

            // some code
            Thread.Sleep(SomeTime);
        }
    }

    public void AddItem(string item)
    {
        lock (_criticalSection)
        {
            _list.Add(item);
        }
    }

    public void RemoveItem(string item)
    {
        lock (_criticalSection)
        {
            _list.Remove(item);
        }
    }

}

Но используя тот же код, я просто изменил метод UpdateControl следующим образом:

private delegate void UpdateControlDelegate(string value);

public void UpdateControl(string value)
{
    if (InvokeRequired)
    {
        BeginInvoke(new UpdateControlDelegate(UpdateControl), value);
    }
    else
    {
        txtAddress.Text = value; // This is in GroupBox1
        txtValue.Text = value; // This is in GroupBox2
    }
}

Это нормально работает. В чем проблема?


person Mohanavel    schedule 17.02.2009    source источник
comment
Я не знаю, как разместить еще один вопрос на той же странице. поэтому я подробно поднял свой вопрос на другой странице. Список приведен ниже stackoverflow.com/questions/556883/deadlock-in-lock   -  person Mohanavel    schedule 17.02.2009
comment
Под вашим вопросом должна быть ссылка для редактирования. Не публикуйте повторяющиеся вопросы и не публикуйте дополнительную информацию в ответе. Отредактируйте исходный вопрос   -  person Binary Worrier    schedule 17.02.2009


Ответы (3)


Проблема почти наверняка в том, что вы получаете блокировку в фоновом потоке, затем вызываете Control.Invoke и вызываете делегата (в потоке пользовательского интерфейса), который пытается получить ту же блокировку. Он не может этого сделать, потому что другой поток удерживает блокировку и будет удерживать блокировку, пока ожидает завершения операции пользовательского интерфейса.

По общему признанию, в опубликованном вами методе UpdateControl нет блокировки, но я подозреваю, что это не полный код - и вы не показали, где вы используете AddItem или RemoveItem.

Отмечу, кстати, что GetThreadHandler () не является потокобезопасным - мне это кажется ошибкой ...

person Jon Skeet    schedule 17.02.2009

Вы вызываете AddItem и RemoveItem из основного потока при вызове UpdateControl из рабочего потока? Это вызовет тупиковые ситуации.

person Jakob Christensen    schedule 17.02.2009

Вот мой код,

public class ValueReader
{
    List<IDataReader> _list = new List<IDataReader>();

    object _criticalSection = new object();

      public ValueReader()
      {
        //Nothign here
      }

      public void Attach(IDataReader reader)
      {
            lock(_criticalSection)
            {
                _list.Add(reader);
            }
      }

      public void Detach(IDataReader reader)
      {
            lock(_criticalSection)
            {
                _list.Remove(reader);
            }
      }

      public void Notify(string value)
      {
            lock(_criticalSection)
            {
                foreach(IDataReader reader in _list)
                {
                    reader.Update(value);
                }
            }
        }

      public void Start()
      {
            new Thread(new ThreadStart(Run)).Start();
      }


      private void Run()
      {
            while(true)
            {

                //generate value
                Notify(value);

                Thread.Sleep(5000);

            }
      } 

}




public interface IDataReader
{
    void UpdateControls(string value);
}

public class FirstClass : IDataReader
{

    ....
    ......  
    ValueReader _reader = null;

    public FirstClass()
    {

        _reader = new ValueReader();
              _reader.Start();
        _reader.Attach(this);

    }

    private void AddToSmartClient()
    {
        // _reader has added to SmartClient's WorkItem
    }


    private delegate void UpdateControlDelegate(string value);

    public void UpdateControl(string value)
    {
        if(txtAddress.InvokeRequired)
        {
            txtAddress.Invoke(new UpdateControlDelegate(UpdateControl), value);
        }
        else
        {
            txtAddress.Text = value;
            txtValue.Text = value;
        }
    }

}


public class SecondClass : IDataReader
{
        ....
    ......
    ValueReader _reader = null;

    public void SecondClass()
    {
        _reader = ReadFromSmartClient();
        _reader.Attach(this);
    }

    private ValueReader ReadFromSmartClient()
    {
        reader = //Get from SmartClient's Workitem.
        return reader
    }

    private delegate void UpdateControlDelegate(string value);

    public void UpdateControl(string value)
    {
        if(InvokeRequired)
        {
            BeginInvoke(new UpdateControlDelegate(UpdateControl), value);
        }
        else
        {
            control1.Text = value;
            control2.Text = value;
        }
    }

}

Некоторое время я вызываю только FirstClass. В этом случае он работает нормально. Через некоторое время я вызвал второй класс, в это время при вызове Attach из secondClass приложение зависло (я отслеживал его появление до блокировки методов Attach (_criticalSection).

Через некоторое время я конвертирую элемент управления обновлением в класс Frist следующим образом

     public void UpdateControl(string value)
{
    if(InvokeRequired)
    {
        BeginInvoke(new UpdateControlDelegate(UpdateControl), value);
    }
    else
    {
        txtAddress.Text = value;
        txtValue.Text = value;
    }
}

Это хорошо работает и после вызова SecondClass. Почему это происходит?

person Mohanavel    schedule 17.02.2009