Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在.NET中编写安全/正确的多线程代码?_C#_.net_Vb.net_Multithreading - Fatal编程技术网

C# 如何在.NET中编写安全/正确的多线程代码?

C# 如何在.NET中编写安全/正确的多线程代码?,c#,.net,vb.net,multithreading,C#,.net,Vb.net,Multithreading,今天我不得不修复一些旧的VB.NET 1.0代码,它使用线程。问题在于从工作线程而不是UI线程更新UI元素。我花了一些时间才发现,我可以使用带有invokererequired的断言来发现问题 除了上面提到的并发修改问题外,还可能遇到死锁、竞争条件等问题由于调试/修复线程问题是一件痛苦的事情,我想知道如何减少这方面的编码错误/错误,以及如何更容易地找到它们。因此,我想要的是: 在编写多线程代码时,有什么好的模式可以遵循吗?应该做什么和不应该做什么? 您使用什么技术来调试线程问题? 如果适用和

今天我不得不修复一些旧的VB.NET 1.0代码,它使用线程。问题在于从工作线程而不是UI线程更新UI元素。我花了一些时间才发现,我可以使用带有invokererequired的断言来发现问题

除了上面提到的并发修改问题外,还可能遇到死锁、竞争条件等问题由于调试/修复线程问题是一件痛苦的事情,我想知道如何减少这方面的编码错误/错误,以及如何更容易地找到它们。因此,我想要的是:

  • 在编写多线程代码时,有什么好的模式可以遵循吗?应该做什么和不应该做什么?
  • 您使用什么技术来调试线程问题?
如果适用和可能,请提供一些示例代码。答案应该与.NET framework(任何版本)相关。

这可能是一个庞大的列表-请阅读Joe Duffy的“优秀”以了解更多详细信息。这简直是一场脑力崩溃

  • 当您拥有锁时,尽量避免调用大量代码
  • 避免锁定类外的代码也可能锁定的引用
  • 如果一次需要获取多个锁,请始终以相同的顺序获取这些锁
  • 在合理的情况下,使用不可变类型-它们可以在线程之间自由共享
  • 除了不可变类型之外,尽量避免在线程之间共享数据
  • 避免尝试使您的类型线程安全;大多数类型不需要是,通常需要共享数据的代码需要控制锁定本身
  • 在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;
    }