我正在与队友讨论锁定 .NET 的问题。他是一个非常聪明的人,在低级和高级编程方面都有广泛的背景,但他在低级编程方面的经验远远超过我。无论如何,他认为,如果可能的话,应该避免在预期处于高负载下的关键系统上使用.NET锁定,以避免“僵尸线程”使系统崩溃的可能性很小。我经常使用锁定,我不知道什么是“僵尸线程”,所以我问。我从他的解释中得到的印象是,僵尸线程是一个已经终止但仍以某种方式保留某些资源的线程。他举了一个关于僵尸线程如何破坏系统的例子,一个线程在锁定某个对象后开始某个过程,然后在某个时候在解锁锁定之前终止。这种情况可能会导致系统崩溃,因为最终,尝试执行该方法将导致线程都等待访问永远不会返回的对象,因为使用锁定对象的线程已死。
我想我明白了这个要点,但如果我离开了基地,请告诉我。这个概念对我来说很有意义。我不完全相信这是 .NET 中可能发生的真实场景。我以前从未听说过“僵尸”,但我确实认识到,在较低级别深入研究的程序员往往对计算基础知识(如线程)有更深入的理解。但是,我确实看到了锁定的价值,并且我见过许多世界级的程序员利用锁定。我自己评估这一点的能力也很有限,因为我知道该语句实际上只是语法糖:lock(obj)
bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }
并且因为 和 被标记.似乎可以想象.NET会执行某种处理来保护线程免受可能产生这种影响的系统组件的暴露,但这纯粹是推测性的,可能只是基于我以前从未听说过“僵尸线程”的事实。所以,我希望我能在这里得到一些反馈:Monitor.Enter
Monitor.Exit
extern
两年多前我问过这个问题。今天发生了这样的事情:
对我来说似乎是一个很好的解释——一个已经终止的线程(因此无法再释放任何资源),但其资源(例如句柄)仍然存在并且(可能)导致问题。
他们肯定会,看,我做了一个!
[DllImport("kernel32.dll")]
private static extern void ExitThread(uint dwExitCode);
static void Main(string[] args)
{
new Thread(Target).Start();
Console.ReadLine();
}
private static void Target()
{
using (var file = File.Open("test.txt", FileMode.OpenOrCreate))
{
ExitThread(0);
}
}
该程序启动一个线程,该线程打开一个文件,然后立即使用.生成的僵尸线程永远不会释放“test.txt”文件的句柄,因此该文件将保持打开状态,直到程序终止(您可以使用进程资源管理器或类似工具进行检查)。“test.txt”的句柄在被调用之前不会释放——事实证明,创建一个泄漏句柄的僵尸线程比我想象的还要困难)Target
ExitThread
GC.Collect
不要做我刚才做的事情!
只要你的代码正确地清理了自己(如果使用非托管资源,请使用安全句柄或等效类),只要你不特意以奇怪而奇妙的方式杀死线程(最安全的方法就是永远不要杀死线程 – 让它们正常终止,或者在必要时通过异常终止), 您拥有类似于僵尸线程的唯一方法是如果出现问题(例如,CLR 中出现问题)。
事实上,创建一个僵尸线程实际上非常困难(我不得不 P/Invoke 进入一个函数,该函数在文档中明确地告诉您不要在 C 之外调用它)。例如,以下(糟糕的)代码实际上不会创建僵尸线程。
static void Main(string[] args)
{
var thread = new Thread(Target);
thread.Start();
// Ugh, never call Abort...
thread.Abort();
Console.ReadLine();
}
private static void Target()
{
// Ouch, open file which isn't closed...
var file = File.Open("test.txt", FileMode.OpenOrCreate);
while (true)
{
Thread.Sleep(1);
}
GC.KeepAlive(file);
}
尽管犯了一些非常可怕的错误,但“test.txt”的句柄仍然会在调用后立即关闭(作为终结器的一部分,在幕后使用 SafeFileHandle 来包装其文件句柄)Abort
file
C.Evenhuis答案中的锁定示例可能是当线程以非奇怪方式终止时无法释放资源(在这种情况下为锁定)的最简单方法,但这可以通过使用语句代替或将释放放在块中轻松修复。lock
finally
参见
lock
我已经清理了一下我的答案,但保留了下面的原始答案以供参考
这是我第一次听说僵尸这个词,所以我假设它的定义是:
已终止但未释放其所有资源的线程
所以给定这个定义,那么是的,你可以在.NET中做到这一点,就像其他语言(C / C++,java)一样。
但是,我不认为这是不在 .NET 中编写线程关键任务代码的好理由。可能还有其他原因决定反对 .NET,但仅仅因为您可以拥有僵尸线程而注销 .NET 对我来说没有意义。僵尸线程在 C/C++ 中是可能的(我什至认为在 C 中搞砸要容易得多),许多关键的线程应用程序都在 C/C++(大量交易、数据库等)。
结论
如果您正在决定使用一种语言,那么我建议您考虑大局:性能、团队技能、日程安排、与现有应用程序的集成等。当然,僵尸线程是你应该考虑的事情,但是由于与 C 等其他语言相比,在 .NET 中实际犯这个错误非常困难,我认为这种担忧将被上面提到的其他事情所掩盖。祝你好运!
原始答案
僵尸†如果您没有编写正确的线程代码,则可能存在。对于其他语言(如C / C++和Java)也是如此。但这并不是不在 .NET 中编写线程代码的理由。
就像任何其他语言一样,在使用某些东西之前要知道价格。它还有助于了解引擎盖下发生的事情,以便您可以预见任何潜在的问题。
无论您使用哪种语言,任务关键型系统的可靠代码都不容易编写。但我肯定在 .NET 中正确完成并非不可能。同样是AFAIK,.NET线程与C / C++中的线程没有太大区别,它使用(或从)相同的系统调用构建,除了一些.net特定的构造(如RWL和事件类的轻量级版本)。
†我第一次听说僵尸这个词,但根据你的描述,你的同事可能指的是一个在没有释放所有资源的情况下终止的线程。这可能会导致死锁、内存泄漏或其他一些不良副作用。这显然是不可取的,但是由于这种可能性而单独挑出.NET可能不是一个好主意,因为它在其他语言中也是可能的。我什至认为在 C/C++ 中比在 .NET 中更容易搞砸(尤其是在没有 RAII 的 C 中如此),但许多关键应用程序都是用 C/C++ 编写的,对吗?所以这真的取决于你的个人情况。如果要从应用程序中提取每一盎司的速度,并希望尽可能接近裸机,则 .NET 可能不是最佳解决方案。如果您的预算紧张,并且与Web服务/现有的.net库/等进行了大量接口,那么.NET可能是一个不错的选择。
现在,我的大部分答案已被以下评论更正。我不会删除答案,因为我需要声誉点,因为评论中的信息可能对读者有价值。
Immortal Blue指出,在.NET 2.0及更高版本中,块不受线程中止的影响。正如Andreas Niedermair评论的那样,这可能不是一个实际的僵尸线程,但以下示例显示了中止线程如何导致问题:finally
class Program
{
static readonly object _lock = new object();
static void Main(string[] args)
{
Thread thread = new Thread(new ThreadStart(Zombie));
thread.Start();
Thread.Sleep(500);
thread.Abort();
Monitor.Enter(_lock);
Console.WriteLine("Main entered");
Console.ReadKey();
}
static void Zombie()
{
Monitor.Enter(_lock);
Console.WriteLine("Zombie entered");
Thread.Sleep(1000);
Monitor.Exit(_lock);
Console.WriteLine("Zombie exited");
}
}
但是,当使用块时,当以这种方式触发时,仍然会执行。lock() { }
finally
ThreadAbortException
事实证明,以下信息仅对 .NET 1 和 .NET 1.1 有效:
如果在块内部发生其他异常,并且恰好在块即将运行时到达,则不会释放锁。正如你提到的,该块被编译为:lock() { }
ThreadAbortException
finally
lock() { }
finally
{
if (lockWasTaken)
Monitor.Exit(temp);
}
如果在生成的块内调用另一个线程,则可能不会释放锁。Thread.Abort()
finally
模板简介:该模板名称为【C# zombies存在吗?在 .NET 中?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。