C# 为什么';避免代码死锁
请解释一下为什么这个代码不会导致死锁C# 为什么';避免代码死锁,c#,wpf,mvvm,locking,task-parallel-library,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 Ob
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);
}
}
静态对象列表锁=新对象();
void StartAsync()
{
System.Threading.Tasks.Task.Factory.StartNew(()=>
{
锁(列表锁)
基于财产变更(“MyList”);
});
}
公共可观测集合MyList
{
得到
{
锁(列表锁)
返回新的ObservableCollection(_myObjectList);
}
}
一些背景细节:程序正在使用MVVM模式,并且MyList绑定到WPF UI上的Datagrid
\u myObjects只是对象的随机列表。
是因为OnPropertyChange只是通知UI它必须从MyList获取新数据并返回而不关心UI是否实际获取数据吗?
我知道OnPropertyChanged是在单独的线程上调用的,但UI存在于单个线程上(不是吗O.O)因此,得到通知的线程也是获得数据的线程。我原以为锁不会因此而释放?要在单个锁上发生死锁,需要两个线程,第一个线程抓住锁,然后等待第二个线程抓住同一个锁。否则不会出现死锁(即,
lock
语句中没有等待其他线程,或者只涉及一个线程)
未来读者注意——以下不是WPF案例中发生的情况——请参见svick的答案。只是在同一线程上有两个锁的无死锁的一般示例:
一种可能的情况是,OnPropertyChanged的侦听器调用
MyList
,以响应同一线程上的同步通知(调用MyList
时签出调用堆栈)。在这种情况下,嵌套的lock
不会执行任何操作,因为请求锁的线程已经持有它。要在单个锁上发生死锁,需要两个线程,第一个线程抓住锁,然后等待第二个线程抓住同一个锁。否则不会出现死锁(即,lock
语句中没有等待其他线程,或者只涉及一个线程)
未来读者注意——以下不是WPF案例中发生的情况——请参见svick的答案。只是在同一线程上有两个锁的无死锁的一般示例:
一种可能的情况是,OnPropertyChanged的侦听器调用
MyList
,以响应同一线程上的同步通知(调用MyList
时签出调用堆栈)。在这种情况下,嵌套的lock
什么也不做,因为请求锁的线程已经持有它。这里的关键实现是PropertyChanged
的处理程序确实在UI线程上调度一些访问MyList
的代码,但是不会等待它完成
因此,一个可能的事件序列是:
StartAsync()
锁定后台线程MyList
的PropertyChanged
PropertyChanged
的处理程序调度在UI线程上访问MyList
的代码PropertyChanged
的处理程序返回MyList
PropertyChanged
的处理程序执行如下操作:
Dispatcher.Invoke(() =>
{
if (e.PropertyName == "MyList")
{
var newList = model.MyList;
// set newList as the current value of some binding
}
});
但事实上,它的作用类似于(唯一的区别是第一行):
这里的关键实现是
PropertyChanged
的处理程序确实安排了一些在UI线程上访问MyList
的代码,但它不会等待它完成
因此,一个可能的事件序列是:
StartAsync()
锁定后台线程MyList
的PropertyChanged
PropertyChanged
的处理程序调度在UI线程上访问MyList
的代码PropertyChanged
的处理程序返回MyList
PropertyChanged
的处理程序执行如下操作:
Dispatcher.Invoke(() =>
{
if (e.PropertyName == "MyList")
{
var newList = model.MyList;
// set newList as the current value of some binding
}
});
但事实上,它的作用类似于(唯一的区别是第一行):
我不是100%确定,但我认为property changed事件只是为wpf绑定引擎添加了一个dispatcher作业,以便稍后在另一个dispatcher作业中检索它。因此,是的,您的锁被释放,因为当事件被引发时,它还没有调用getter。如果您仍然在同一调用堆栈中,则可以在调试器中检查这一点。同样,我也不确定实际情况是否如此。我不是100%确定,但我认为property changed事件只是为wpf绑定引擎添加了一个dispatcher作业,以便稍后在另一个dispatcher作业中检索它。因此,是的,您的锁被释放,因为当事件被引发时,它还没有调用getter。如果您仍然在同一调用堆栈中,则可以在调试器中检查这一点。我也不确定事实是否如此。@svick,你的回答似乎非常接近现实。出于历史原因保留这一条。@svick,你的答案似乎非常接近现实。出于历史原因保留这一条。那么我当时的假设是半正确的?OnPropertyChanged简单地告诉UI获取新数据,然后返回,因此正如您所说的那样