为什么代码不会死锁

时间:2013-03-27 02:07:05

标签: c# wpf mvvm locking task-parallel-library

有些人可以向我解释为什么这段代码不会导致死锁?

static Object listlock = new Object();

void StartAsync()
{
    System.Threading.Tasks.Task.Factory.StartNew(() => 
        {
            lock(listlock)
                base.OnPropertyChanged("MyList");
        });
}

 public ObservableCollection<MyObjects> MyList
 {
    get
    {
         lock(listlock)
             return  new ObservableCollection<MyObjects>(_myObjectList);
    }
 }

一些背景细节:
该程序使用 MVVM 模式, MyList 绑定到 WPF用户界面上的数据网格 _myObjects 只是一个随机的对象列表。

是因为 OnPropertyChange 只是通知用户界面它必须从MyList获取新数据并且只是返回而不关心UI是否实际获得数据?是的我知道OnPropertyChanged在一个单独的线程上调用,但是UI存在于一个线程上(不是它),因此获得通知的线程也是获取数据的线程。我会认为锁定因为那个而无法释放?

2 个答案:

答案 0 :(得分:3)

对于单锁上发生的死锁,你需要2个线程,这样第一个线程抓住锁并等待让第二个线程获取同一个锁。否则你不会遇到死锁(即没有等待 lock语句中的其他线程,或者只涉及一个线程。)

未来读者注意 - 以下不是WPF案例中的情况 - 请参阅svick的回答。在同一个线程上只有2个锁没有死锁的通用示例:

一种可能的情况是OnPropertyChanged的监听器调用MyList以响应同一线程上的同步通知(在调用MyList时检出调用堆栈)。在这种情况下,嵌套的lock什么都不做,因为请求锁的线程已经拥有它。

答案 1 :(得分:3)

这里的关键实现是PropertyChanged的处理程序确实安排了一些在UI线程上访问MyList的代码,但不等待它完成

所以,一个可能的事件序列是:

  1. StartAsync()获取后台线程的锁定。
  2. {li> PropertyChanged MyList被提出。
  3. PropertyChanged的处理程序调度访问UI线程上的MyList的代码。
  4. PropertyChanged的处理程序返回。
  5. 锁被释放。
  6. UI线程尝试访问MyList
  7. UI线程无需等待即可获取锁定,因为没有其他线程拥有它。
  8. ...
  9. 另一种表达方式:我认为你期望的是PropertyChanged的处理程序是这样的:

    Dispatcher.Invoke(() =>
    {
        if (e.PropertyName == "MyList")
        {
            var newList = model.MyList;
            // set newList as the current value of some binding
        }
    });
    

    但事实上,它确实有类似的东西(唯一的区别是第一行):

    Dispatcher.BeginInvoke(() =>
    {
        if (e.PropertyName == "MyList")
        {
            var newList = model.MyList;
            // set newList as the current value of some binding
        }
    });