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工作.