I'm currently exploring threading implementation in C# WinForms and I created this simple app:
I'm just wondering why the memory usage of this app keeps growing after I start, stop, start, and stop again the application. I'm having a thought that my thread instance doesn't really terminate or abort when I press the stop button. Please consider my code below, and any help or suggestions will be greatly appreciated.
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();
}
}
}
Thread-based multitasking deals with the concurrent execution of pieces of the same program. A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called a thread, and each thread defines a separate path of execution.
Can we write multithreading programs in C? Unlike Java, multithreading is not supported by the language standard. POSIX Threads (or Pthreads) is a POSIX standard for threads. Implementation of pthread is available with gcc compiler.
OK, there are several recommendations:
Make your flag volatile... if you don't, then an update to the flag may never be seen by the thread.
volatile bool stopThread = false;
Set the IsBackground
property to true: it forces the thread to be terminated if the application exits, otherwise you may get a "ghost thread" which exists even after the application has closed.
thread1.IsBackground = true;
thread1.Start();
If the thread just started sleeping, then you will abort it even before it has had a chance to read the flag... furthermore you don't want to use Abort
because:
...if one thread calls Abort on another thread, the abort interrupts whatever code is running. There is a chance the thread could abort while a finally block is running, in which case the finally block is aborted. There is also a chance that a static constructor could be aborted. In rare cases, this might prevent instances of that class from being created in that application domain.
So instead of using abort, I would recommend that you call Interrupt
and you handle the exception inside the thread:
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();
}
}
You're leaking threads every time you click the Start button... if thread1
is already assigned a thread and you assign another thread to it, then the previous one will continue to exist. You need to stop the previous thread before you start another one.
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();
}
Finally, you need to handle the interrupts in your thread:
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
}
}
I think that's about it... oh... actually, there is one more thing: Thread Carefully! ;)
New threads are pretty expensive in terms of memory. The default stack size is 1MB I believe for each new thread. Others have mentioned that calling Abort on a thread isn't really a good way to end a thread (and if the thread is blocking, it may not even abort the thread.) However, in your case I don't think Abort is the reason you are seeing memory grow.
Basically, the .NET garbage collector probably just has not gotten around to freeing up the memory it has allocated. You could try forcing a collection using GC.Collect() but you shouldn't do this in production code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With