我有这个示例代码,我正在试图弄清楚发生了什么。
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();
}
}
}
答案 0 :(得分:2)
首先,.Net框架在线程和清理资源方面并不差。不知道你在哪里听到这个,资源的链接会很好。
在您的代码后面遇到一些麻烦:
如果您反复拨打DoSomething
,为什么要处置并创建新的AutoResetEvent
?您可以重复使用该实例并在循环结束时进行清理。
messageTarget未在未直接使用Thread
的版本中使用。
在直接使用Thread
的版本中,您在每个循环中创建并启动新的Thread
- 为什么?你将最终得到一堆正在运行的线程。没有什么可以等待他们完成,他们很可能在你的循环结束时运行。最终他们的睡眠时间将结束并且他们将退出,但是你的循环将在第一次线程睡眠完成之前完成。
我怀疑句柄与您创建的正在运行的线程有关。您需要等待线程完成,例如使用Thread.Join
。你的例子并没有真正展示出对线程有用的东西,只是创造了很多线程。
<强>更新强>
在回答您的问题更新时,通常您会使用ThreadPool
或TPL
(使用引擎盖下的线程池),而不是直接创建线程。 Threadpool就在那里,因为他们有效地管理线程资源。
如果你直接创建一个Thread
,可能会分配一些句柄(我相信CLR可以自由地重用一些线程资源,所以图片很复杂)。
答案 1 :(得分:0)
如果您使用线程池,就像在原始代码中一样,线程池将自动限制您要创建的线程和事件的数量。 如果您显式创建线程,BCL将执行以下操作:它将创建所有线程。每个线程都会创建一个句柄,当你不再使用线程对象时它将被关闭(即当它完成运行时。我的猜测是你必须加入线程才能告诉CLR你没有需要手柄,但这只是猜测。)