Terminating/joining a thread in C# [duplicate] - c#

This question already has answers here:
c# Thread issue using Invoke from a background thread
(5 answers)
Closed 6 years ago.
I can't seem to be able to kill my thread in C#. The program seems to get stuck in an infinite loop on the FormClosing event.
EDIT // I'm attempting to end the thread and close the whole program when the FormClosing event gets fired.
Here's the code:
public partial class Form1 : Form
{
private Thread thread;
private volatile bool threadRunning = true;
public Form1()
{
InitializeComponent();
}
private void Loop()
{
Console.WriteLine(threadRunning);
while (threadRunning)
{
MethodInvoker mi = delegate { timeLabel.Text = TimeWriterSingleton.Instance.OutputTime(); };
Invoke(mi);
}
}
private void Form1_Load(object sender, EventArgs e)
{
thread = new Thread(Loop);
thread.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
threadRunning = false;
thread.Join();
}
}

Your Join blocked the GUI thread, and your Invoke in the other thread is waiting for your GUI thread to process the delegate.
A quick fix would be to use BeginInvoke instead of Invoke, thus posting rather than sending the window message.
Alternatively, don't join. The purpose of that code is to clean up after yourself, why do you care when the thread dies?
A 3rd fix would be to just gut the thread, either through Thread.Abort or Environment.Exit. It might skip some clean up, but your particular code shouldn't care and the point is to exit anyway.
Edit: working code using BeginInvoke follows:
private void Loop()
{
while (threadRunning)
{
BeginInvoke(new MethodInvoker(() => timeLabel.Text = DateTime.Now.ToString()));
Thread.Sleep(100);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
threadRunning = false;
thread.Join();
}
The issue with the original code is that it's running as fast as your CPU allows, filling the message queue to the point where the GUI thread can't keep up. Updating Windows controls is very expensive, compared to simply adding a number to a queue. So I added a pause between UI updates to let the GUI thread breathe.
To the downvoters, I'd be curious why you're doing it. Nothing I said is factually wrong.

I decided to switch to using a timer. The code now looks like this, and the application works:
public partial class Form1 : Form
{
private System.Timers.Timer timer;
public Form1()
{
InitializeComponent();
timer = new System.Timers.Timer(60000);
}
private void Form1_Load(object sender, EventArgs e)
{
timeLabel.Text = TimeWriterSingleton.Instance.OutputTime();
timer.Elapsed += TimerElapsed;
timer.Enabled = true;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
timeLabel.Text = TimeWriterSingleton.Instance.OutputTime();
}
}

Actually using the BeginInvoke() is not bad idea. It might look like that:
private void Form1_Load(object sender, EventArgs e)
{
thread = new Thread(() => Loop(this));
thread.Start();
}
private void Loop(Form1 form)
{
while (threadRunning && !form.IsDisposed)
{
MethodInvoker mi = delegate() { timeLabel.Text = /* Some text */ ; };
BeginInvoke(mi);
// Let sleep some time...
Thread.Sleep(1);
}
}
private void Form1_FormClosing_1(object sender, FormClosingEventArgs e)
{
threadRunning = false;
thread.Join();
}

Related

How can I make repeated calls to an async method with an on/off switch

In a WinForm application, I have an On/Off switch for a background process.
On Click, the program launch a process and restart it when it's finish, till you use the Off switch.
The following code is the working attempt that had multiple issues.
From Damien_The_Unbeliever comments:
suspend threads which means that they stay around forever, and implementing looping via recursion which could easily cause a stack overflow.
public partial class frmMain
{
Thread thread;
bool isRunning = false;
public frmMain()
{
InitializeComponent();
}
private void OnOffSwitch_Click(object sender, EventArgs e)
{
if (!isRunning)
{
btnSwitch.Text = "Stop";
isRunning = true;
thread = new Thread(doLoop);
thread.IsBackground = true;
thread.Start();
}
else
{
if (thread.IsAlive)
thread.Suspend();
btnSwitch.Text = "Start";
isRunning = false;
}
}
public void doLoop()
{
ClearScreenLogic.Run();
if (AutoReconnect)
ReconnectLogic.Run();
// Etc..
doLoop();
}
I am trying to switch from this working solution to background worker.
Implement your doLoop in the DoWork event of the BackGroundWorker and make sure you handle cancelation. Make sure to set the properties of your backgroundworker to WorkerReportprogress and WorkerSupportCancellation to true;
This is what you would need:
private void button1_Click(object sender, EventArgs e)
{
// on and off
if (backgroundWorker1.IsBusy)
{
// cancel if we have not already done so
if (!backgroundWorker1.CancellationPending)
{
backgroundWorker1.CancelAsync();
}
}
else
{
// start the background work
button1.BackColor = Color.Yellow;
backgroundWorker1.RunWorkerAsync();
}
}
// this runs on a background thread
// do not do stuff with the UI here
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int progress = 0;
// stop looping if cancellation is requested
while (!backgroundWorker1.CancellationPending)
{
// make it nice
backgroundWorker1.ReportProgress(progress);
ClearScreenLogic.Run();
if (AutoReconnect)
ReconnectLogic.Run();
// Etc..
progress++; // for feedback
}
}
// tell the use something is going on, this runs on the UI thread
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString();
}
// we're done, tell the user so
// this runs on the UI thread
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.BackColor = Color.Green;
label1.Text = "cancelled";
}
When implemented correctly your users will see something like this:

How can I display a loading control while a process is waiting for be finished?

I decided to use this third-party component to make a simple loading control in my windows form.
http://www.codeproject.com/Articles/14841/How-to-write-a-loading-circle-animation-in-NET
This works fine when turns on and off changing the property "Active" to true or false in a single request (one per time). The problem is when a process is waiting to be served, and I pretend to Active the loadingControl before the process starts and turn off when I "think" that the process has to be finished. When I do it, the image loading is shown as a static image. (Without animation).
I'm sorry for this question, I'm new in C#. But I think that I need to use Threads or something similar.
So my general code is this:
using [libraries here]...;
namespace [namespace here]
{
Public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.loadingCircle1.Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(showLoading));
this.loadingCircle1.Visible = true;
t.Start();
//Import an Excel
t.Abort();
}
public void showLoading()
{
loadingCircle1.Active = true;
loadingCircle1.RotationSpeed = 10;
}
}
}
But Always the Loading shows as a static image without the animation.
You create a thread, which simply sets two properties and then ends. The t.Abort will probably do nothing, since the thread will have been exited by that time. Even worse, you import the excel file on the UI thread, which blocks any animation and freezes the complete UI.
This is how you should make it:
Remark: Of course if your form is responsive, you must disable/enable the controls and prepare to the case what happens if your form is being closed during the load.
1. Using threads
If you really want to explicitly use threads, do it like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Thread workerThread = null;
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation (I used a progress bar, start your circle here)
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job and the timer, which polls the thread
btnImport.Enabled = false;
workerThread = new Thread(LoadExcel);
workerThread.Start();
timer1.Interval = 100;
timer1.Start();
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (workerThread == null)
{
timer1.Stop();
return;
}
// still works: exiting
if (workerThread.IsAlive)
return;
// finished
btnImport.Enabled = true;
timer1.Stop();
progressBar1.Visible = false;
workerThread = null;
}
}
2. Background worker
The BackgroundWorker can throw an event when it is finished:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += BackgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
}
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job
btnImport.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
LoadExcel();
}
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
3. Using async-await
This is the simplest one.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void btnImport_Click(object sender, EventArgs e)
{
// start the waiting animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// simply start and await the loading task
btnImport.Enabled = false;
await Task.Run(() => LoadExcel());
// re-enable things
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
I'd recommend to use async/await (for C# 5.0):
private void button1_Click(object sender, EventArgs e){
ImportAsync();
}
private async Task ImportAsync(){
// UI-thread
showLoading();
this.loadingCircle1.Visible = true;
// wait until task will be finished
await Task.Run(() => {
// different non-blocking thread for all the hard work, but without UI-stuff
// import an Excel
});
// going back to UI-thread
this.loadingCircle1.Visible = false;
}

BackgroundWorker never does anything?

This is the code that I am trying to execute, but stepping through my code I never see any progress indicated or updated on my windows form showing progressbar1. This is my 1st attempt in getting a background worker to function properly, and all I have is a windows form with one button on it and this is all of the code involved in the project.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private int i = 0;
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = false;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ReadySteadyGo();
worker.ReportProgress((i * 10));
FinalizeAndFinish();
worker.ReportProgress((i * 10));
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Text = "Done!";
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Text = (e.ProgressPercentage.ToString() + "%");
}
private void ReadySteadyGo()
{
Thread.Sleep(100000);
}
private void FinalizeAndFinish()
{
Thread.Sleep(1000);
}
}
It appears that you are using Thread.Sleep() to simulate a long-running operation. There are a few things you should consider based on your code example:
When the backgroundWorker1.RunWorkerAsync(); is executed, it starts working on another thread. Thus, if you are debugging interactively and you have not set a breakpoint in the backgroundWorker1_DoWork method, you are not likely to see this code execute.
When the Thread.Sleep(100000) executes, it essentially means that the background worker will pause for 100 seconds - so you need to make sure you are waiting at least that long to see the UI updated.
Also, as per Hans Passant's comment, consider the following:
Nor can you see it doing anything, there's no point to assigning the
ProgressBar.Text property since it doesn't display text. Set Value
instead.
I recreated your example in Visual Studio and am hitting a breakpoint in backgroundWorker1_DoWork so the multi-threading is working properly, you just need to do proper processing?
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}

C# thread invoke freezing window

This is code freezing current window. How to made non freezing this form.
public partial class Form1 : Form
{
Thread t;
int s = 0;
public Form1()
{
InitializeComponent();
label2.Text = "Push the Button";
button1.Text = "Push me!";
button1.Click += new EventHandler(button1_Click);
this.Controls.Add(label2);
this.Controls.Add(button1);
}
void button1_Click(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(RunMe));
t.Start();
}
private void RunMe()
{
if (!InvokeRequired)
{
while(true)
{
label2.Text = s.ToString();
s++;
Task.Delay(10000).Wait(10000);
}
}
else
{
Invoke(new ThreadStart(RunMe));
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
As others have stated, you are using the UI thread to execute an endless loop, you should use the Timer control, it was built for exactly what you're doing.
https://social.msdn.microsoft.com/Forums/windows/en-US/43daf8b2-67ad-4938-98f7-cae3eaa5e63f/how-to-use-timer-control-in-c?forum=winforms
Your code can benefit from using async-await if you are using .net 4.5. Using await you won't have to start a seperate thread for your RunMe method, it will free up your UI thread to do other work but the SynchronizationContext is captured so that you don't have to use Invoke to update the UI. For how that works, see this blog.
I think you should be able to rewrite your code like this:
async void button1_Click(object sender, EventArgs e)
{
// kicks off the RunMe method and returns
await RunMe();
}
private Task RunMe()
{
while(true)
{
label2.Text = s.ToString();
s++;
await Task.Delay(10000);
}
}
Despite the infinite while loop the method only wakes up to update the label and runs for a very short time in the UI thread.
You should call the invoke only for updating the label like this:
while(true)
{
if (!InvokeRequired)
{
label2.Text = s.ToString();
}
else
{
Invoke(new Action(()=>{label2.Text = s.ToString();}));
}
s++;
Task.Delay(10000).Wait(10000);
}

C#, Form.Timer interval value adjusting in a worker thread

I want to modify interval value of timer which is instance of System.Microsoft.Timer from a worker thread
When i change this value in the thread running worker thread, Timer is stopped.
let see my source code
private void Scan_Screen(object sender, EventArgs e)
{
textBox1.Text += "a";
}
private void button1_Click(object sender, EventArgs e)
{
g_RECEIVER_timer = new System.Windows.Forms.Timer();
g_RECEIVER_timer.Enabled = true;
g_RECEIVER_timer.Interval = TIMER_INTERVAL;
g_RECEIVER_timer.Tick += new EventHandler(Scan_Screen);
}
private void button2_Click(object sender, EventArgs e)
{
g_Control_Thread = new Thread(new ParameterizedThreadStart(Control_Message_Receiver));
g_Control_Thread.Start(200);
}
//thread function
public void Control_Message_Receiver(object v)
{
g_RECEIVER_timer.Stop();
g_RECEIVER_timer.Interval = 200;
g_RECEIVER_timer.Enabled = true;
g_RECEIVER_timer.Tick += new EventHandler(Scan_Screen);
}
Why this happening is occurred? Also how can i make this run? (I want to adjust interval value of timer in the worker thread)
You need to invoke this Control_Message_Receiver on the UI thread since you have spawned another worker thread and you are accessing objects on the UI thread context.
And don't need to re-declare the Tick event on your worker thread method.
Look at this snippet below:
private void Scan_Screen(object sender, EventArgs e)
{
textBox1.Text += "a";
}
private void button1_Click(object sender, EventArgs e)
{
g_RECEIVER_timer = new System.Windows.Forms.Timer();
g_RECEIVER_timer.Enabled = true;
g_RECEIVER_timer.Interval = 1000;
g_RECEIVER_timer.Tick += new EventHandler(Scan_Screen);
}
private void button2_Click(object sender, EventArgs e)
{
Thread g_Control_Thread = new Thread(new ParameterizedThreadStart(Control_Message_Receiver));
g_Control_Thread.Start(1);
}
//thread function
public void Control_Message_Receiver(object v)
{
//timer1.Stop(); //why stop? -- remove this instead
IntervalChange((int)v); //call this method and invoke it on the UI thread
g_RECEIVER_timer.Enabled = true;
//timer1.Tick += new EventHandler(Scan_Screen); // -- remove this
}
delegate void intervalChanger(int time);
void ChangeInterval(int time)
{
g_RECEIVER_timer.Interval = time;
}
void IntervalChange(int time)
{
this.Invoke(new intervalChanger(ChangeInterval), new object[] {time}); //invoke on the UI thread
}
It may be better to use System.Threading.Timer. This one can be readjusted from any thread. Note, however, that the timer callback runs in a threadpool thread. So you have to use Invoke, if you need to access the GUI from this callback.

Categories