C# 如何在.NET中编写安全/正确的多线程代码?
今天我不得不修复一些旧的VB.NET 1.0代码,它使用线程。问题在于从工作线程而不是UI线程更新UI元素。我花了一些时间才发现,我可以使用带有invokererequired的断言来发现问题 除了上面提到的并发修改问题外,还可能遇到死锁、竞争条件等问题由于调试/修复线程问题是一件痛苦的事情,我想知道如何减少这方面的编码错误/错误,以及如何更容易地找到它们。因此,我想要的是:C# 如何在.NET中编写安全/正确的多线程代码?,c#,.net,vb.net,multithreading,C#,.net,Vb.net,Multithreading,今天我不得不修复一些旧的VB.NET 1.0代码,它使用线程。问题在于从工作线程而不是UI线程更新UI元素。我花了一些时间才发现,我可以使用带有invokererequired的断言来发现问题 除了上面提到的并发修改问题外,还可能遇到死锁、竞争条件等问题由于调试/修复线程问题是一件痛苦的事情,我想知道如何减少这方面的编码错误/错误,以及如何更容易地找到它们。因此,我想要的是: 在编写多线程代码时,有什么好的模式可以遵循吗?应该做什么和不应该做什么? 您使用什么技术来调试线程问题? 如果适用和
- 在编写多线程代码时,有什么好的模式可以遵循吗?应该做什么和不应该做什么?
- 您使用什么技术来调试线程问题?
- 当您拥有锁时,尽量避免调用大量代码
- 避免锁定类外的代码也可能锁定的引用
- 如果一次需要获取多个锁,请始终以相同的顺序获取这些锁
- 在合理的情况下,使用不可变类型-它们可以在线程之间自由共享
- 除了不可变类型之外,尽量避免在线程之间共享数据
- 避免尝试使您的类型线程安全;大多数类型不需要是,通常需要共享数据的代码需要控制锁定本身
- 在WinForms应用程序中:
- 不要在UI线程上执行任何长时间运行或阻塞操作
- 不要从UI线程以外的任何线程触摸UI。(使用BackgroundWorker、Control.Invoke/BeginInvoke)
- 尽可能避免线程局部变量(又称线程静态)-它们可能导致意外行为,特别是在ASP.NET上,不同的线程可能提供请求(搜索“线程敏捷性”和ASP.NET)
- 别装聪明。无锁并发代码很难得到正确的结果
- 记录类型的线程模型(和线程安全性)
- Monitor.Wait几乎应该总是与某种检查结合使用,在while循环中(即while(我不能继续)Monitor.Wait(Monitor))
- 每次使用Monitor.Pulse和Monitor.Pulse时,请仔细考虑两者之间的差异
- 插入线程。睡眠使问题消失永远不是真正的解决办法
- 将“并行扩展”和“协调和并发运行时”视为简化并发的方法。并行扩展将成为.NET4.0的一部分
在调试方面,我没有太多的建议。使用Thread.Sleep来增加看到比赛状态和死锁的机会是可行的,但是在你知道应该把它放在哪里之前,你必须对错误有一个合理的理解。日志记录非常方便,但是不要忘记代码进入了一种量子状态——通过日志记录观察它几乎必然会改变它的行为 我不确定这对您正在使用的特定应用程序有多大帮助,但这里有两种从函数式编程中借来的编写多线程代码的方法: 不可变对象 如果需要在线程之间共享状态,则状态应该是不可变的。如果一个线程需要对对象进行更改,它将使用更改创建对象的全新版本,而不是改变对象的状态 不变性并不固有地限制您可以编写的代码类型,也不是低效的。有许多不可变堆栈的实现,各种构成映射和集合基础的不可变树,以及其他类型的不可变数据结构,许多(如果不是全部的话)不可变数据结构与它们的可变对应结构一样高效 由于对象是不可变的,所以一个线程不可能在您的鼻子下改变共享状态。这意味着您不需要获取锁来编写多线程代码。这种方法消除了与死锁、活锁和竞争条件相关的一系列错误 Erlang风格的消息传递 您不需要学习该语言,但可以看看Erlang,看看它是如何实现并发的。Erlang应用程序可以无限扩展,因为每个进程都与其他进程完全分离(注意:这些进程并不完全是进程,但也不完全是线程) 进程启动并简单地旋转一个循环等待消息:消息以元组的形式接收,然后进程可以对元组进行模式匹配,以查看消息是否有意义。进程可以发送其他消息,但它们与接收消息的人无关
这种风格的优点是消除了锁,当一个进程失败时,它不会关闭整个应用程序。下面是对Erlang风格并发性的一个很好的总结:以下是编写高质量(易于阅读和理解)多线程代码的步骤:
使用先进先出。很多。这是硬件程序员的古老秘密,它不止一次拯救了我的生命。似乎没有人回答如何调试多线程程序的问题。
public const int MaxMessages = 0x100;
string[] messages = new string[MaxMessages];
int messagesIndex = -1;
public void Trace(string message) {
int thisIndex = Interlocked.Increment(ref messagesIndex);
messages[thisIndex] = message;
}