使用线程时处理增长 - 为什么?

时间:2011-08-08 08:43:46

标签: c# multithreading resources handle

我有这个示例代码,我正在试图弄清楚发生了什么。

    private static AutoResetEvent autoEvent = new AutoResetEvent(false);
    private static Thread t1;
    static void DoSomething()
    {
        Console.WriteLine("Main starting.");

        ThreadPool.QueueUserWorkItem(
            WorkMethod, autoEvent);

        // Wait for work method to signal.
        autoEvent.WaitOne();

        // trying out does resource cleanup by using dispose and null where possible
        autoEvent.SafeWaitHandle.Dispose();
        t1 = null;
        autoEvent = null;
        autoEvent = new AutoResetEvent(false);
        Console.WriteLine("Work method signaled.\nMain ending.");
    }

    static Action messageTarget; 

    static void WorkMethod(object stateInfo)
    {
        Console.WriteLine("Work starting.");
          // This line is going to change
          messageTarget =  delegate()
                 {
                     Thread.Sleep(new Random().Next(100, 2000));
                 };

        // Signal that work is finished.
        Console.WriteLine("Work ending.");
        ((AutoResetEvent)stateInfo).Set();
    }

这样可以正常工作并在100个循环的for循环后创建7个句柄(使用TestApi的内存快照处理计数)。

现在有趣的行为是这样的: 当我将代理包装在一个线程

中时
        t1 = new Thread
            (
          delegate()
                 {
                     Thread.Sleep(new Random().Next(100, 2000));
                 });
        t1.Start();

应用程序完成大约295个句柄!

我听说.net框架在线程和清理资源方面很差,这是正确的吗?当应用程序完成时,某些线程仍然可以在后台运行但是确定这有点极端行为?

我的问题是如何导致如此高的手数? (请注意,这是模拟另一个应用程序中的某些行为,并不是为了生产,而是为了理解在使用线程时句柄数量增长如此剧烈的原因)

使用Thread.Join解决方案

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Test.LeakDetection;

namespace FaultDetection
{
     public partial class Form1 : Form
     {
      private Process process;

    public Form1()
    {
        InitializeComponent();

        foreach (Process clsProcess in Process.GetProcesses())
        {

            if (clsProcess.ProcessName.Contains("FaultDetection"))
            {
                //if the process is found to be running then we
                //return a true
                process = clsProcess;
            }
        }

        MemorySnapshot s1;
        if (process != null)
        {
            s1 = MemorySnapshot.FromProcess(process.Id);

            for (int i = 0; i < 100; i++)
            {
                DoSomething();
                MemorySnapshot s2 = MemorySnapshot.FromProcess(process.Id);

                // Compare the two memory snapshots and generate a diff.
                // Then display the diff to the console.
                MemorySnapshot diff = s2.CompareTo(s1);

                Console.WriteLine("\tHandle Count: {0}", diff.HandleCount);
                label1.Text = "Handle Count: "+ diff.HandleCount + "\n";
            }
        }
    }

    private static AutoResetEvent autoEvent = new AutoResetEvent(false);
    private static Thread t1;
    private static List<Thread> threadReferences;

    static void DoSomething()
    {
        Console.WriteLine("Main starting.");

        ThreadPool.QueueUserWorkItem(
            WorkMethod, autoEvent);

        // Wait for work method to signal.
        autoEvent.WaitOne();

        t1.Join();
        autoEvent.SafeWaitHandle.Dispose();
        t1 = null;
        autoEvent = null;
        autoEvent = new AutoResetEvent(false);
        Console.WriteLine("Work method signaled.\nMain ending.");
    }

    static Action messageTarget; 

    static void WorkMethod(object stateInfo)
    {
        Console.WriteLine("Work starting.");
        t1 = new Thread
            (
          delegate()
                 {
                     Thread.Sleep(new Random().Next(100, 2000));
                 });
        t1.Start();
        //messageTarget = delegate() { Thread.Sleep(new Random().Next(100, 2000)); };

        // Signal that work is finished.
        Console.WriteLine("Work ending.");
        ((AutoResetEvent)stateInfo).Set();
    }
}

}

2 个答案:

答案 0 :(得分:2)

首先,.Net框架在线程和清理资源方面并不差。不知道你在哪里听到这个,资源的链接会很好。

在您的代码后面遇到一些麻烦:

  1. 如果您反复拨打DoSomething,为什么要处置并创建新的AutoResetEvent?您可以重复使用该实例并在循环结束时进行清理。

  2. messageTarget未在未直接使用Thread的版本中使用。

  3. 在直接使用Thread的版本中,您在每个循环中创建并启动新的Thread - 为什么?你将最终得到一堆正在运行的线程。没有什么可以等待他们完成,他们很可能在你的循环结束时运行。最终他们的睡眠时间将结束并且他们将退出,但是你的循环将在第一次线程睡眠完成之前完成。

  4. 我怀疑句柄与您创建的正在运行的线程有关。您需要等待线程完成,例如使用Thread.Join。你的例子并没有真正展示出对线程有用的东西,只是创造了很多线程。

    <强>更新

    在回答您的问题更新时,通常您会使用ThreadPoolTPL(使用引擎盖下的线程池),而不是直接创建线程。 Threadpool就在那里,因为他们有效地管理线程资源。

    如果你直接创建一个Thread,可能会分配一些句柄(我相信CLR可以自由地重用一些线程资源,所以图片很复杂)。

答案 1 :(得分:0)

如果您使用线程池,就像在原始代码中一样,线程池将自动限制您要创建的线程和事件的数量。 如果您显式创建线程,BCL将执行以下操作:它将创建所有线程。每个线程都会创建一个句柄,当你不再使用线程对象时它将被关闭(即当它完成运行时。我的猜测是你必须加入线程才能告诉CLR你没有需要手柄,但这只是猜测。)