为什么模式从不构建在.NET框架中的不同线程更新UI?

Din*_*nah 10 .net multithreading asynchronous winforms

我知道"为什么我的这个框架喜欢/不喜欢xyz?" 问题有点危险,但我想看看我错过了什么.

在WinForms中,您无法从另一个线程更新UI.大多数人使用这种模式:

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
}
Run Code Online (Sandbox Code Playgroud)

更聪明的是这种模式:

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);
}
Run Code Online (Sandbox Code Playgroud)

无论使用哪种,每个人都必须编写样板代码来处理这个令人难以置信的常见问题.为什么.NET Framework没有更新为我们这样做呢?这个代码库的区域是冻结的吗?是否会破坏向后兼容性?当某些代码在版本N中以单向方式工作而在版本N + 1中以不同方式工作时,是否会引起混淆?

Eri*_*ert 14

我觉得提起为什么首先有一个UI线程可能会很有趣.它是为了降低UI组件生产的成本,同时提高其正确性和稳健性.

线程安全的基本问题是,如果在读取发生时写入线程已完成一半,则可以在读取线程上观察到私有状态的非原子更新是半完成的.

为了实现线程安全,您可以执行许多操作.

1)显式锁定所有读写.优点:最大的灵活性; 一切都适用于任何线程.缺点:最痛苦; 一切都必须一直锁定.锁可以争用,这使它们变慢.编写死锁非常容易.编写处理重新入门的代码非常容易.等等.

2)仅允许在创建对象的线程上进行读写.您可以在多个线程上拥有多个对象,但是一旦在线程上使用了对象,那么这是唯一可以使用它的线程.因此,不同的线程不会同时进行读写操作,因此您无需锁定任何内容.这是"公寓"模型,它是绝大多数UI组件构建的模型.唯一需要锁定的状态是由不同线程上的多个实例共享的状态,这很容易做到.

3)仅允许在拥有线程上进行读写,但是当没有正在进行的读写操作时,允许一个线程明确地将所有权移交给另一个.这是"租用"模型,它是Active Server Pages用于回收脚本引擎的模型.

由于绝大多数UI组件都是在公寓模型中编写的,并且使所有这些组件都是自由线程是很痛苦和困难的,因此您不得不在UI线程上完成所有UI工作.