Stopping Threading - c#

I am new to winforms programming and I am starting to work with threads.
I have managed to start a thread, but now I want to be able to click on a cancel button to stop the thread.
Here is my code so far...
This starts the thread:
private void btnSessions_Click(object sender, EventArgs e)
{
Thread downloadThread = new Thread(new ThreadStart(DownloadThread));
downloadThread.Start();
}
This is the thread:
void DownloadThread()
{
// Do the work
}
This is the button I want to use to cancel the thread:
private void btnCancel_Click(object sender, EventArgs e)
{
// Stop the thread
}
Can anyone help me work out what I need to put in btnCancel_Click please?

You should use the Task Parallel Library (TPL) for this, which supports a natural way of canceling tasks:
private CancellationTokenSource _tokenSource2;
private CancellationToken _token;
private void btnSessions_Click(object sender, EventArgs e)
{
_tokenSource2 = new CancellationTokenSource();
_token = _tokenSource2.Token;
Task task = Task.Run(() => DownloadThread(), _token);
}
private void DownloadThread()
{
while (true)
{
//do work
//cancel if needed
if (_token.IsCancellationRequested)
{
_token.ThrowIfCancellationRequested();
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
// Stop the thread
_tokenSource2.Cancel();
}
More about canceling tasks: http://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx
Why you should not use Thread.Abort: What's wrong with using Thread.Abort()

You need to make the downloadThread a field in your object:
Thread downloadThread;
private void btnSessions_Click(object sender, EventArgs e)
{
downloadThread = new Thread(new ThreadStart(DownloadThread));
downloadThread.Start();
}
void DownloadThread()
{
// Do the work
}
private void btnCancel_Click(object sender, EventArgs e)
{
downloadThread.Abort();
}
A background worker would be a better solution for ui related processing.

It's better don't use Thread and especially Thread.Abort for task like this. C# has high abstract wrapper to hide threads. Just use Task and CancellationToken.
Here is example:
var cts = new CancellationTokenSource(); // define in class
CancellationToken ct = cts.Token;
private void btnSessions_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => DownloadThread(), ct ); // start task
}
private void DownloadThread()
{
// You need to check this at some point where cancel may occur
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
}
private void btnCancel_Click(object sender, EventArgs e)
{
cancelToken.Cancel(false); // cancel task
}
More information can be found at msdn

Related

async function running in sequence WPF

so I am making a small WPF app.
I am new to C# and Multithreading, I want to run certain methods in sequence but because one of the methods is Async it does not run in sequence.
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
if (!OpenFile()) return; // opens a file dialog and ensure format is correct
await Task.Run(() =>
{
// some heavy task which I run here so that I dont freeze the UI
});
}
private void TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
LoadButton_Click(sender, e);
SaveCareerInfoButton_Click(sender, e); // I want this line to wait for load to finish
LoadButton_Click(sender, e);
ImportCareerInfoButton_Click(sender, e); // I want this line to wait for the second load to finish
}
Await these calls as well, refactor your code a bit.. extract handler's content to a separate method and don't pass senders and args between handlers
private Task Load()
{
if (!OpenFile()) return;
return Task.Run(() =>
{
// some heavy task which I run here so that I dont freeze the UI
});
}
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
await Load();
}
private async void TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
await Load();
// refactor your code to not pass sender, e.. SaveCareer();
SaveCareerInfoButton_Click(sender, e);
await Load();
// refactor your code to not pass sender, e.. ImportCareer();
ImportCareerInfoButton_Click(sender, e);
}
You can use await to wait an async function to finish and make function TheFunctionIwantToRunInSeqeuence to async Task return type:
private async Task TheFunctionIwantToRunInSeqeuence(object sender, RoutedEventArgs e)
{
await LoadButton_Click(sender, e);
SaveCareerInfoButton_Click(sender, e); // I want this line to wait for load to finish
await LoadButton_Click(sender, e);
ImportCareerInfoButton_Click(sender, e); // I want this line to wait for the second load to finish
}

wpf c# backgroundworker wait until finished

I have several textboxes in my wpf application. The LostFocus-Event of each textbox starts a backgroundworker to send the data to a connected serial port.
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data);
}
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> data = (List<object>)e.Argument;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do some things after worker completed
}
At this point, everything is working fine.
But sometimes I have to send two data-points directly after each other and there I have a problem.
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data1);
//wait until backgroundworker has finished
online_mode_send_worker.RunWorkerAsync(data2);
}
The Backgroundworker is still running and I get an exception thrown.
Is it possible to wait after the first online_mode_send_worker.RunWorkerAsync(data) until it has finished and then start the second online_mode_send_worker.RunWorkerAsync(data)?
while(online_mode_send_worker.isBusy); is not working because the main-thread is blocking and the RunWorkerCompleted() is not thrown and so the Backgroundwoker is always busy.
I have found something like this, but Application.DoEvents() is not available in wpf.
while (online_mode_send_worker.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
Here is a rough idea of what I mentioned in the comments.
public class Messenger {
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
private readonly ConcurrentQueue<object> messages;
public Messenger() {
messages = new ConcurrentQueue<object>();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
}
public void SendAsync(object message) {
if (online_mode_send_worker.IsBusy) {
messages.Enqueue(message);
} else {
online_mode_send_worker.RunWorkerAsync(message);
}
}
public Action<object> MessageHandler = delegate { };
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e) {
if (MessageHandler != null)
MessageHandler(e.Argument);
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
object nextMessage = null;
if (messages.Count > 0 && messages.TryDequeue(out nextMessage)) {
online_mode_send_worker.RunWorkerAsync(nextMessage);
}
}
}
You have a queue to hold on to messages that were sent while the background worker was busy and have the worker check the queue for any pending messages when it has completed doing its work.
The messenger can be used like this.
private Messenger messenger = new Messenger();
private void Initialize() { //I would expect this to be in the constructor
messenger.MessageHandler = MessageHandler;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
messenger.SendAsync(data);
}
private void MessageHandler(object message)
{
List<object> data = (List<object>)message;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
It seems that I missed the serial stuff. So what you want to do is synchronize your asynchronuouscalls:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => mySerialDevice1.WriteData(data1));
Task.Run(() => mySerialDevice1.WriteData(data2));
}
public class SerialDevice
{
public Port Port { get; set; }
public object _LockWriteData = new object();
public void WriteData(string data)
{
lock(_LockWriteData)
{
Port.WriteLine(data);
}
}
}
also see:
https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
https://msdn.microsoft.com/en-us/library/de0542zz(v=vs.110).aspx
ORIGINAL ANSWER
You can use Task instead of Backgroundworker.
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => OnlineModeSendData(data1));
Task.Run(() => OnlineModeSendData(data2));
}
private void OnlineModeSendData(List<string> data)
{
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0]+ XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
I also would like to suggest that you make real objects instead of passing string arrays as arguments.
For Example send BlinkLedRequest:
public class BlinkLedRequest
{
public int LedId{get;set;}
public int DurationInMilliseconds {get;set}
}
and a corresponding method:
public void SendBlinkLed(BlickLedRequest request)
{
....
}
I think your should use RunWorkerCompleted event and add a delegate:
online_mode_send_worker.RunWorkerCompleted += (s, ev) =>
{
if (ev.Error != null)
{
//log Exception
}
//if(conditionToBrake)
// return;
online_mode_send_worker.RunWorkerAsync(data2);
};
online_mode_send_worker.RunWorkerCompleted(data1);
Make sure you put there a condition to avoid infinite loop.
I'd say that if you MUST wait until after the first "job" is done, that what you want is Task.ContinueWith() and change your interface accordingly. The msdn page is good for it IMO, but watch out that you're waiting on the "correct" task object. Hint: it's the return value of ContinueWith() that you should call Wait() on. This is a good pattern to do for launching a Task and then waiting for it later as long as you can keep the Task that is returned so you can wait on it.
For a more generic "I only want one background thread doing things in the order they're added, and I want to wait until they're ALL done and I know when I'm done adding." I would suggest using a BlockingCollection<Action> with only one thread consuming them. An example of how to do that is found in this other answer.
Update:
bw.RunWorkerAsync(data1);
//wait here
bw.RunWorkerAsync(data2);
Is not good aproach, because UI will be blocked on time of waiting. Better:
bw.RunWorkerAsync(new object[] { data1, data2 }); //or new object[] { data1 } if no data2
Original answer:
I advice not to use construction: while (bw.Busy) { ... } (it consumes cpu time), use synchronization objects, for example, ManualResetEvent
BackgroundWorker is great class, but does not support waiting. Just create addition object for waiting:
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bool wasError;
ManualResetEvent e = null;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e != null)
return;
wasError = false;
e = new ManualResetEvent(false); //not signaled
bw.RunWorkerAsync(data1);
e.Wait(); //much better than while(bw.Busy())
if (!wasError)
bw.RunWorkerAsync(data2);
e = null;
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//background work in another thread
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//catch exception here
wasError = true;
}
e.Set(); //switch to signaled
}
If you need only call twice you can do this:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data2);
}
But if you need to queue commands you need rewrite in another way Using Task.
One Task where inside it you will have a for-loop where you will send your data through serial port sequentially.
https://msdn.microsoft.com/pt-br/library/system.threading.tasks.task(v=vs.110).aspx

How to stop a thread for a time consuming function

I'm trying to do a windows app and I have a function that takes several minutes to complete the task. I have a start button and
I'd like to add a stop button in order to stop the processing of the function whenever I want to stop it.
I'm trying with the code below, but I'm not sure how to abort the Thread1 inside btnStop since Thread1 is marked as "does not exists
in current context".
May you please suggest me/point me in rigth direction in how would be a good way to do this. Thanks in advance.
namespace SampleStartStop
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
Thread Thread1 = new Thread(SlowFunction);
Thread1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
Thread1.Abort();
MessageBox.Show("Processing canceled");
}
public void SlowFunction()
{
var end = DateTime.Now + TimeSpan.FromSeconds(10);
while (DateTime.Now < end)
{ }
MessageBox.Show("Process finished");
}
}
}
Update:
Hi KCdod, thanks for your help, When I only declare thread as global variable I get "An unhandled exception of type 'System.NullReferenceException'
occurred in SampleStartStop.exe".
Hi Alexei, thanks for the correction. Thanks zerkms and Alexei for share about cancellation tokens. Following the example in link you shared
I was able to write the code below. It seems to work but I'd like the approbal of you experts if it needs some change or if it is fine.
The only doubt regarding the current code is, if Stop button is pressed it stops the processing fine, but if I click start button again,
nothing happens and I need to close and open again the App in order to get working again start button, is this normal?
The other doubt is in the part inside "The listener". In MSDN example they put "// Perform cleanup if necessary.", so, what kind of clean up
they are talking about?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Create the token source.
CancellationTokenSource cts = new CancellationTokenSource();
private void btnStart_Click(object sender, EventArgs e)
{
// Pass the token to the cancelable operation.
ThreadPool.QueueUserWorkItem(new WaitCallback(SlowFunction), cts.Token);
}
private void btnStop_Click(object sender, EventArgs e)
{
// Request cancellation.
cts.Cancel();
// Cancellation should have happened, so call Dispose.
cts.Dispose();
MessageBox.Show("Processing canceled");
}
public void SlowFunction(object obj)
{
CancellationToken token = (CancellationToken)obj;
var end = DateTime.Now + TimeSpan.FromSeconds(10);
while (DateTime.Now < end)
{
// Thread 2: The listener
if (token.IsCancellationRequested)
{
// Perform cleanup if necessary.
//...
// Terminate the operation.
break;
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
}
Update:
Thanks Alexei for your correction, I've modified the code with your suggestions and this time works nice. the code is as below. I only have an issue since in my real code, the Function needs a string argument to work and I don't know how to call it inside the part "WaitCallback(SlowFunction)" and how to define the function in the code, since here is defined like this "public void SlowFunction(object obj) {...}" and in my real function is like this "public void SlowFunction(string str)". I think that I'd need to ask in a new question this issue.
namespace SampleStartStop
{
public partial class Form1 : Form
{
// Create the token source.
CancellationTokenSource cts = new CancellationTokenSource();
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
// Pass the token to the cancelable operation.
cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(SlowFunction), cts.Token);
}
private void btnStop_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
cts = null;
MessageBox.Show("Processing canceled");
}
}
public void SlowFunction(object obj)
{
CancellationToken token = (CancellationToken)obj;
var end = DateTime.Now + TimeSpan.FromSeconds(5);
while (DateTime.Now < end)
{
if (token.IsCancellationRequested)
{
break;
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
}
}
There is no good way to terminate thread that is not cooperating. Indeed Thread.Abort will do that but at price of potentially leaving non-disposed objects and abandoned synchronization primitives thus potentially destabilizing your program.
Fix for your immediate problem - move Thread1 to be class level member instead of local variable. You'll need to check if it is already set/cleared:
public partial class Form1 : Form {
...
Thread thread1 = null;
private void btnStart_Click(object sender, EventArgs e)
{
if (thread1 != null)
{
thread1.Abort();
}
thread1 = new Thread(SlowFunction);
Thread1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
if (thread1 != null)
{
thread1.Abort();
thread1 = null;
MessageBox.Show("Processing canceled");
}
}
It will be much better if you can make "slow function" to cooperate in termination - i.e. by periodically checking some value. Check Cancellation tokens for .Net way of doing so.
You can declare your thread, Thread Thread1; as global variable. In your current code your Thread1 scope limits to btnStart_Click() event function.
namespace SampleStartStop
{
public partial class Form1 : Form
{
Thread Thread1=null;
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
Thread1 = new Thread(SlowFunction);
Thread1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
Thread1.Abort();
MessageBox.Show("Processing canceled");
}
public void SlowFunction()
{
var end = DateTime.Now + TimeSpan.FromSeconds(10);
while (DateTime.Now < end)
{ }
MessageBox.Show("Process finished");
}
}
}
Additional - Thread abort is not GOOD but you can use it.

How to access Thread Name from Other function?

I want to access the thread from other function in the same class.
For example
private void timer1_Tick(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ThreadStart(Send1));
thread1.Start();
}
private void stop_btn_Click(object sender, EventArgs e)
{
thread1.Stop();
}
I wan to access thead1 from stop_btn_Click event. Both functions are in the same class Form1.
Declare private Thread thread1; on the class level rather than method
class ClassName
{
private Thread workerThread = null;
private void timer1_Tick(object sender, EventArgs e)
{
this.workerThread = new Thread(new ThreadStart(Send1));
workerThread.Start();
}
private void stop_btn_Click(object sender, EventArgs e)
{
this.workerThread.Stop();
}
}
By looking at the method name timer1_Tick() I can assume that you are simulating a timer behaviour. Take a look at the System.Timers.Timer and System.Threading.Timer classes perhaps you'll find them more useful for your case.
You need to store the thread in a private field in the form.
You also need to figure out what should happen if the user clicks Start twice; you may want to check whether the thread is already running, or use a list of threads.
you could take the variable outside the method (moving it into the class as a field):
private Thread thread1 = null;
void timer1_Tick(object sender, EventArgs e)
{
thread1 = new Thread(new ThreadStart(Send1));
thread1.Start();
}
private void stop_btn_Click(object sender, EventArgs e)
{
if (thread1 != null)
thread1.Stop();
}

c#: controlling access to object from different threads

How do I control when a thread is permitted to access an object and when it is not.
For example, if I have situation like below, I want to make sure that when I am doing something with objFoo in my ButtonClick event, I should not be able to touch objFoo from my doSomethingWithObjFoo method.
private void button1_Click(object sender, EventArgs e) {
// doing something with objFoo
}
private void timer1_Tick(object sender, EventArgs e) {
Thread T = new Thread(new ThreadStart(doSomethingWithObjFoo));
T.Start();
}
private void doSomethingWithObjFoo(){
// doing something else with objFoo
}
The easiest way is perhaps to use lock:
private object _fooLock = new object();
private void button1_Click(object sender, EventArgs e) {
lock(_fooLock)
{
// doing something with objFoo
}
}
private void timer1_Tick(object sender, EventArgs e) {
Thread T = new Thread(new ThreadStart(doSomethingWithObjFoo));
T.Start();
}
private void doSomethingWithObjFoo(){
lock(_fooLock)
{
// doing something else with objFoo
}
}
There are other options as well, such as using a ReaderWriterLockSlim.
That what we use lock for.
Thread Synchronization is a must read.
public class TestThreading
{
private System.Object lockThis = new System.Object();
public void Process()
{
lock (lockThis)
{
// Access thread-sensitive resources.
}
}
}

Categories

Resources