我知道“为什么我的这个框架像/不像 xyz?”问题有点危险,但我想看看我错过了什么。
在 WinForms 中,您不能从另一个线程更新 UI。大多数人使用this pattern :
private void EventHandler(object sender, DirtyEventArgs e)
{
if (myControl.InvokeRequired)
myControl.Invoke(new MethodInvoker(MethodToUpdateUI), e);
else
MethodToUpdateUI(e);
}
private void MethodToUpdateUI(object obj)
{
// Update UI
}
更聪明的是this pattern :
public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return call(isi);
}
public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired)
isi.BeginInvoke(call, new object[] { isi });
else
call(isi);
}
不管使用哪种,每个人都必须编写样板代码来处理这个令人难以置信的常见问题。那么,为什么没有更新 .NET Framework 来为我们执行此操作?是不是代码库的这个区域被卡住了?是否担心它会破坏向后兼容性?当某些代码在版本 N 中以一种方式工作而在版本 N+1 中以不同方式工作时,是否会引起混淆?
最佳答案
我认为首先提到为什么会有一个 UI 线程可能会很有趣。这是为了降低 UI 组件的生产成本,同时提高它们的正确性和健壮性。
线程安全的基本问题是,如果在读取发生时写入线程已完成一半,则可以观察到私有(private)状态的非原子更新在读取线程上已完成一半。
为了实现线程安全,您可以做很多事情。
1) 显式锁定所有读写。优点:最大的灵 active ;一切都适用于任何线程。缺点:最大的痛苦;一切都必须一直锁定。可以争用锁,这使它们变慢。写死锁很容易。编写无法很好地处理重入的代码是很容易的。等等。
2) 只允许在创建对象的线程上进行读写。您可以在多个线程上拥有多个对象,但是一旦在线程上使用了一个对象,那就是唯一可以使用它的线程。因此不会同时在不同的线程上读写,所以你不需要锁定任何东西。这是“公寓”模型,也是绝大多数 UI 组件构建时所期望的模型。唯一需要锁定的状态是不同线程上的多个实例共享的状态,这很容易做到。
3) 只允许在拥有的线程上进行读写,但当没有正在进行的读写时,允许一个线程显式地将所有权移交给另一个线程。这是“租用”模型,它是 Active Server Pages 用来回收脚本引擎的模型。
由于绝大多数 UI 组件都是为在公寓模型中工作而编写的,并且使所有这些组件都成为自由线程是痛苦和困难的,因此您不得不在 UI 线程上完成所有 UI 工作。
关于.net - 为什么从不同线程更新 UI 的模式没有内置到 .NET 框架中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4004918/