简单线程,为什么会发生这种情况?(C#WinForms)
我目前正在探索C#WinForms中的线程实现,我创建了一个简单的应用程序: 我只是想知道,为什么在我启动、停止、启动和再次停止应用程序之后,这个应用程序的内存使用量一直在增长。我认为当我按下停止按钮时,我的线程实例不会真正终止或中止。请考虑下面的代码,非常感谢您的帮助或建议。p>简单线程,为什么会发生这种情况?(C#WinForms),c#,multithreading,C#,Multithreading,我目前正在探索C#WinForms中的线程实现,我创建了一个简单的应用程序: 我只是想知道,为什么在我启动、停止、启动和再次停止应用程序之后,这个应用程序的内存使用量一直在增长。我认为当我按下停止按钮时,我的线程实例不会真正终止或中止。请考虑下面的代码,非常感谢您的帮助或建议。p> using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadingTest
{
public partial class Form1 : Form
{
private delegate void TickerDelegate(string s);
bool stopThread = false;
TickerDelegate tickerDelegate1;
Thread thread1;
public Form1()
{
InitializeComponent();
tickerDelegate1 = new TickerDelegate(SetLeftTicker);
}
private void Form1_Load(object sender, EventArgs e)
{
thread1 = new Thread(new ThreadStart(disp));
thread1.Start();
}
void disp()
{
while (stopThread == false)
{
listBox1.Invoke(tickerDelegate1, new object[] { DateTime.Now.ToString() });
Thread.Sleep(1000);
}
}
private void SetLeftTicker(string s)
{
listBox1.Items.Add(s);
}
private void btnStop_Click(object sender, EventArgs e)
{
stopThread = true;
if (thread1.IsAlive)
{
thread1.Abort();
}
}
private void btnStart_Click(object sender, EventArgs e)
{
stopThread = false;
thread1 = new Thread(new ThreadStart(disp));
thread1.Start();
}
private void btnCheck_Click(object sender, EventArgs e)
{
if (thread1.IsAlive)
{
MessageBox.Show("Is Alive!");
}
}
private void btnClear_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
}
}
}
在btnStop_Click中,您将标志设置为true,然后立即检查它是否仍处于活动状态。这不会给线程一个自然终止的机会。相反,您应该使用Join(正如hydrogen所建议的)在线程上执行等待,如果它超时(出于任何原因),则中止该线程。新线程在内存方面非常昂贵。我认为每个新线程的默认堆栈大小是1MB。其他人提到,在线程上调用Abort并不是结束线程的好方法(如果线程阻塞,它甚至可能不会中止线程)。但是,在您的情况下,我不认为Abort是内存增长的原因
volatile bool stopThread = false;
基本上,.NET垃圾收集器可能只是没有腾出时间来释放它分配的内存。您可以尝试使用GC.Collect()强制收集,但不应在生产代码中执行此操作。好的,有几点建议: 使用易失性标志 让你的旗帜不稳定。。。如果没有,那么线程可能永远看不到对标志的更新
volatile bool stopThread = false;
设为背景
将IsBackground
属性设置为true:如果应用程序退出,它将强制终止线程,否则即使在应用程序关闭后,您也可能会得到一个“重影线程”
thread1.IsBackground = true;
thread1.Start();
如果线程刚刚开始休眠,那么您将在它有机会读取标志之前中止它。。。此外,您不想使用中止
,因为:
…如果一个线程调用另一个线程的中止
线程,中止中断任何
代码正在运行。这是一个机会
线程可能会中止,而
块正在运行,在这种情况下
最后,块被中止。有
还有一个机会,一个静态
构造函数可能被中止。难得
在某些情况下,这可能会阻止
该类不会在该类中创建
应用程序域
使用中断而不是中止
因此,我建议您调用Interrupt
并在线程内处理异常,而不是使用abort:
private void btnStop_Click(object sender, EventArgs e)
{
// have another method for re-use
StopThread();
}
private void StopThread()
{
stopThread = true;
// the time out is 100 ms longer than the thread sleep
thread1.Join(1100);
// if the thread is still alive, then interrupt it
if(thread1.IsAlive)
{
thread1.Interrupt();
}
}
不要“泄漏”螺纹
每次单击“开始”按钮都会泄漏线程。。。如果已为thread1
分配了一个线程,并且您为其分配了另一个线程,则前一个线程将继续存在。在启动另一个线程之前,需要停止上一个线程
private void btnStart_Click(object sender, EventArgs e)
{
// stop the previous thread
StopThread();
// create a new thread
stopThread = false;
thread1 = new Thread(new ThreadStart(disp));
thread1.IsBackground = true;// set it to background again
thread1.Start();
}
处理中断
最后,您需要处理线程中的中断:
void disp()
{
try
{
while (stopThread == false)
{
listBox1.Invoke(tickerDelegate1, new object[] { DateTime.Now.ToString() });
Thread.Sleep(1000);
}
}
catch(ThreadInterruptedException)
{
// ignore the exception since the thread should terminate
}
}
我想就是这样。。。哦事实上,还有一件事:小心穿线强>;) 首先,您应该重命名form1和按钮1-4,使其具有有意义的名称;在这种情况下,您很幸运,通过阅读事件代码,可以很明显地看出哪些按钮对应于哪些数字。第二,尽管我知道这可能只是尝试线程,但正确的方法是使用计时器,而不是休眠一秒钟的线程。谢谢,我会接受你的建议。我更喜欢在线程上设置超时执行
.Join()
,而不是执行.Abort()
。如果超时过期,您可以将.Abort()
作为最后手段。如果您需要在长时间.Sleep()
期间中断它,那么您可以执行.interrupt()
操作,但请确保捕获线程中断
异常。@hydrogen:这个可以解决我的问题吗?嗯。。。如果你不介意的话,先生,你能举个例子吗?我试过GC.Collect(),看起来不错。应用程序的内存使用似乎不再增长。但正如你所说,这是解决问题的一种方法,而不是真正的答案。答案是没有问题。NET的垃圾收集器是由内存需求触发的。换句话说,在进程中看到未使用的内存挂起是完全正常的(也是可以接受的)。当需要内存时,它将被释放。好的,先生,非常感谢您的回答。。。我会试试你的代码。如果你一直按start,OP的代码会泄漏线程,但他说他正在执行start、stop、start、stop等操作,所以这不能解释内存增长的原因。我认为这只是正常的GC行为,因为当他使用GC.Collect时,内存被回收。但是还是有好的建议。@Josh,我不知道为什么内存在增长,你对GC行为的猜测可能是正确的。。。“开始”按钮的问题实际上是对线程可能泄漏的另一个领域的评论。谢谢你指出这一点!