1

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

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 UI上的Datagrid_myObjects只是一个随机的对象列表。 是因为OnPropertyChange只是通知 UI 它必须从 MyList 获取新数据并且只是返回而不关心 UI 是否实际获取数据?我知道 OnPropertyChanged 是在单独的线程上调用的,但 UI 存在于单个线程上(不是吗),因此获得通知的线程也是获取数据的线程。我会认为锁不会因此而无法释放?



4

2 回答 2

3

要在单个锁上发生死锁,您需要 2 个线程,这样第一个线程获取锁并等待第二个线程获取相同的锁。否则您将不会陷入死锁(即语句中 没有等待其他线程lock,或者只涉及一个线程)。

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

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

于 2013-03-27T02:17:25.813 回答
3

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

因此,一个可能的事件序列是这样的:

  1. StartAsync()锁定后台线程。
  2. PropertyChangedforMyList被提出。
  3. 调度在 UI 线程上PropertyChanged访问的代码的处理程序。MyList
  4. 返回的处理程序PropertyChanged
  5. 锁被释放。
  6. UI 线程尝试访问MyList.
  7. UI 线程无需等待即可获得锁,因为没有其他线程拥有它。
  8. …</li>

另一种说法:我认为您期望的是处理程序执行以下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
    }
});
于 2013-03-27T18:51:40.820 回答