.net - 为什么从不同线程更新 UI 的模式没有内置到 .NET 框架中?

标签 .net winforms multithreading asynchronous

我知道“为什么我的这个框架像/不像 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/

相关文章:

c# - 超链接可见性的 ASP.NET 声明性绑定(bind)不起作用

c# - 获取 NumericUpDown 的值会移动插入符位置,我可以停止吗?

c# - 如何将多行文本添加到 ListBox 项?

c - 使用 OpenSSL C 库在多线程中生成椭圆曲线 key 对 (EC_KEY_generate_key)

Java线程同步——synchronized关键字的放置

.net - 创建新的 SQLCommand 或重复使用相同的 SQLCommand

c# - 清除和重新填充列表框时闪烁

c# - 如何从用户控件中的主窗体读取属性

java - 使用 SwingWorker 显示加载动画

c# - 使用 RX 组成命令总线