处理.Net线程中的泄漏

Jak*_*ake 5 .net c# multithreading

这实际上咬了我几次.如果你做这样的简单代码:

    private void button1_Click(object sender, EventArgs e)
    {

        for (int i = 0; i < 1000000; i++)
        {
            Thread CE = new Thread(SendCEcho);
            CE.Priority = ThreadPriority.Normal;
            CE.IsBackground = true;
            CE.Start();
            Thread.Sleep(500);
            GC.Collect();
        }
    }

    private void SendCEcho()
    {
        int Counter = 0;

        for (int i = 0; i < 5; i++)
        {
            Counter++;
            Thread.Sleep(25);
        }
    }
Run Code Online (Sandbox Code Playgroud)

运行此代码并观察手柄飞!Thread.Sleep是这样你可以关闭它,它不会接管你的电脑.这应该保证在下一个线程启动之前启动的线程死掉.调用GC.Collect(); 什么也没做.根据我的观察,这个代码在正常刷新时每次刷新任务管理器时都会丢失10个句柄.

无效SendCEcho()函数中的内容无关紧要,如果需要,则计为5.当线程死亡时,有一个手柄无法清理.

对于大多数程序而言,这并不重要,因为它们不会长时间运行.在我创建的一些程序中,它们需要运行数月和数月.

如果反复运用此代码,最终可能会将句柄泄漏到Windows操作系统变得不稳定的位置,并且它将无法运行.需要重新启动.

我确实通过使用线程池和这样的代码解决了这个问题:

ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadJobMoveStudy));
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么.Net中存在这样的泄漏,为什么它存在这么长时间?既然喜欢1.0?我在这里找不到答案.

Bri*_*sen 5

你正在创建永不结束的线程.for循环SendCEcho永远不会终止,因此线程永远不会结束,因此无法回收.如果我修复你的循环代码,那么线程完成并按预期回收.我无法使用下面的代码重现问题.

static void SendCEcho()
{
    int Counter = 0;

    for (int i = 0; i < 5; i++)
    {
        Counter++;
        Thread.Sleep(25);
    }
}
Run Code Online (Sandbox Code Playgroud)


Han*_*ant 5

    for (int i = 0; i < 5 + i++; )
Run Code Online (Sandbox Code Playgroud)

相当奇怪的错字,你还没有重新创造你真正的问题.有一个,Thread对象消耗5个操作系统句柄,其内部终结器释放它们..NET类通常具有Dispose()方法,以确保可以提前释放此类句柄,但Thread没有.这是勇敢的设计,这样的Dispose()方法会非常难叫.

所以不得不依赖终结器是一项艰难的要求.在具有"SendEcho"方法的程序中,并且不执行任何其他操作,您将面临垃圾收集器永远不会运行的风险.所以终结者无法完成它的工作.然后由您自己调用GC.Collect().你会考虑这样做,比如你开始的1000个线程.或者使用ThreadPool.QueueUserWorkItem()或Task.Run(),以便回收线程,即逻辑方法.

使用Perfmon.exe验证GC确实不运行.为您的程序添加.NET CLR Memory>#Gen 0 Collections计数器.


Cla*_*len 4

GC.WaitForPendingFinalizers();尝试在调用 GC.Collect(); 之后添加 我想这会让你得到你想要的。完整来源如下:

编辑

另请看一下 2005 年的内容:https://bytes.com/topic/net/answers/106014-net-1-1-possible-1-0-also-threads-leaking-event-handles-bug。和你的代码几乎一模一样。

using System;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 1000000; i++)
        {
            Thread CE = new Thread(SendCEcho);
            CE.Priority = ThreadPriority.Normal;
            CE.IsBackground = true;
            CE.Start();
            Thread.Sleep(500);
            CE = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    public static void SendCEcho()
    {
        int Counter = 0;
        for (int i = 0; i < 5; i++ )
        {
            Counter++;
            Thread.Sleep(25);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • `Collect()` 将运行集合,但可终结的对象将被放入终结队列中。它们的终结器只会在将来的某个时刻由终结线程运行。调用“WaitForPendingFinalizers()”会执行您所期望的操作:阻塞当前线程,直到终结器线程执行其操作。 (2认同)