In my solution I got a user interface where some word automation is started by a buttonclick (lets call that button wordStart). I want to break this word automation with another buttonclick (lets call that button wordBreak).
However when I click the wordStart the user interface freezes while doing the work and it's not possible for me to click the wordBreak button.
I'm still a bit new to programming so for me this must be because the application is single threaded or atleast I could solve it with multithreading.
So this is a 2 in 1 question.
1. Is it possible to stop the execution of code with a single threaded application?
2. How do I stop the execution of code?
For question number 2 I looked a bit around the internet and found these methods which I think will work, but other suggestions are welcome:
Application.Exit
Application.Shutdown
Environment.Exit
EDIT:
As I thought this should be done with multi threading. I don't have that much experience with that so I've added this code to the question if anyone would like to help me out here. In the meantime I will look for a solution to this myself.
private void generateButton_Click(object sender, EventArgs e)
{
//Thread or backgroundworker should handle this event?
commandsChosed(); //Event to be throwed - this starts the word automation
}
private void stopButton_Click(object sender, EventArgs e)
{
//Stop/pause the working thread
}
No, by using only a single thread it gets blocked until the execution is finished. If you want to be able to cancel/pause it, you need to use another thread for the operation. For instance you can use BackgroundWorker.
Just wanted to post my answer to my own question here in case anyone had a similar problem.
As suggested by others on this question I wasn't able to implement it with a backgroundworker since it doesn't allow OLE functions like use of clipboard and such - but this is specific for what my thread is handling. A backgroundworker could definitely be useful in a lot of situations - but it can't be set to STA since it's from the threadpool.
Thread workerThread;
private void generateButton_Click(object sender, EventArgs e)
{
generateButton.Visible = false;
stopButton.Visible = true;
//Setting up a background thread
workerThread = new Thread(new ThreadStart(handleGenerateButtonClick));
workerThread.SetApartmentState(ApartmentState.STA); //In STA state the thread can use OLE functions like clipboard and handle some UI components as well.
workerThread.IsBackground = true; //It shuts down if the mainthread shuts down
workerThread.Start();
try
{
//Checking whether the currentThread is not the workerThread before blocking the currentThread until workerThread has terminated
if (Thread.CurrentThread != workerThread)
{
//Wait until workerThread has terminated
workerThread.Join();
}
//Sets the window buttons after workerThread has finished
if (!workerThread.IsAlive)
{
generateButton.Visible = true;
stopButton.Visible = false;
}
}
catch
{
}
}
private void stopButton_Click(object sender, EventArgs e)
{
generateButton.Visible = true;
stopButton.Visible = false;
//Stops the worker thread
workerThread.Abort();
}
Related
Suppose you have a search textbox and have a search algorithm attached to the TextChanged event, that runs with a BackgroundWorker. If there comes a new character in the textbox, i need to cancel the previous search and run it again.
I tried using events in between the main thread and the bgw, from this previous question, but I still get the error "currently busy and cannot run multiple tasks concurrently"
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
private void txtSearch_TextChanged(object sender, EventArgs e)
{
SearchWithBgw();
}
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
void bgw_Search_DoWork(object sender, DoWorkEventArgs e)
{
Search(txtSearch.Text, e);
}
private void Search(string aQuery, DoWorkEventArgs e)
{
int i = 1;
while (i < 3) // simulating search processing...
{
Thread.Sleep(1000);
i++;
if (bgw_Search.CancellationPending)
{
_resetEvent.Set(); // signal that worker is done
e.Cancel = true;
return;
}
}
}
EDIT To reflect answers. DonĀ“t reuse the BackgroundWorker, create a new one:
private void SearchWithBgw()
{
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
bgw_Search = new BackgroundWorker();
bgw_Search.WorkerSupportsCancellation = true;
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
bgw_Search.RunWorkerAsync();
}
When the _resetEvent.WaitOne() call completes, the worker thread isn't actually done. It is busy returning from DoWork() and waiting for an opportunity to run the RunWorkerCompleted event, if any. That takes time.
There is no reliable way to ensure the BGW is completed in a synchronous way. Blocking on IsBusy or waiting for the RunWorkerCompleted event to run is going to cause deadlock. If you really want to use only one bgw then you'll have to queue the requests. Or just don't sweat the small stuff and allocate another bgw. They cost very little.
Create a new background worker if the old one exists.
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
Also I know you put fake code in, but you want to make sure you set _resetEvent when the code completes normally too.
Do not reuse a Backgroundworker. It is a cheap resource, it is not a Thread.
make sure your Bgw code stops, yours looks OK. The Bgw will release the Thread to the pool.
but in the mean time, create a new Task/Bgw for a new job.
You may want to unsubscribe your Completed event from the old Bgw.
I think you should consider not cancelling the background worker.
If you cancel requests and the user types faster than your server returns queries, he will not see suggestions until he is finished typing.
In interactive scenarios like this, It could be better to show responses that run behind with what the user's typing. Your user will know he can stop typing if the word he has in mind is your suggestions list.
This will be also better for your server when it is busy, because instead of many cancelled requests, who will cost something but that are ultimately not shown, there will be fewer requests whose response you actually use.
I ran into similar issues with (3d) rendering applications, where the beginner's mistake is to cancel and rerender on every mousemove. This lead to a lot of computation and little interactive feedback.
What happens when more than one thread tries to call a form method using Invoke which updates form controls at the same time in Winforms?
static thCount = 0;
private void button1_Click(object sender, EventArgs e)
{
System.Threading.Thread t1 = new System.Threading.Thread(start);
System.Threading.Thread t2 = new System.Threading.Thread(start);
t1.Start();
t2.Start();
}
private void start()
{
System.Threading.Thread.Sleep(300);
Invoke(new MethodInvoker(guiUpdate));
}
private void guiUpdate()
{
this.label1.Text = "Updated.." + (thCount++);
this.label1.Update();
}
private void Form1_Load(object sender, EventArgs e)
{
this.label1.Text = System.Threading.Thread.CurrentThread.Name;
}
Try it out! :) You'll find that neither of them can update the UI from a background thread, instead they need to use Control.BeginInvoke to invoke work on the UI thread, in which case they will execute in the order that they call BeginInvoke.
Either of the thread will not be able to update the GUI.
You might get cross thread exception if you do not check 'InvokeRequired'.
if you still want these threads to access the same method, you can use Mutual Exclusion concept.
You can find more on Mutual Exclusion here.
This question on stack overflow also explain Mutual Exclusion in detail.
Invoke blocks until the thread has finished executing the update method.
However, this is actually only a message to the GUI thread to do this and it waits until it is done. Since the GUI thread can only execute one method at a time there is no real simultaneous execution. Nothing bad happens, but the behaviour may depend on the sequence of execution.
The sequence of execution, however, depends on which thread ever finished some guaranteed atomic (lock) operation.
I'm going crazy with a simple code in which I use a BackgroundWorker to automate the basic operations. Should I add a content to the clipboard.
After executing this code in the method of the BackgroundWorker:
Clipboard.SetText (splitpermutation [i]);
I get an error that explains the thread must be STA, but I do not understand how to do.
Here more code: (not all)
private readonly BackgroundWorker worker = new BackgroundWorker();
private void btnAvvia_Click(object sender, RoutedEventArgs e)
{
count = lstview.Items.Count;
startY = Convert.ToInt32(txtY.Text);
startX = Convert.ToInt32(txtX.Text);
finalY = Convert.ToInt32(txtFinalPositionY.Text);
finalX = Convert.ToInt32(txtFinalPositionX.Text);
incremento = Convert.ToInt32(txtIncremento.Text);
pausa = Convert.ToInt32(txtPausa.Text);
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync();
[...]
}
private void WorkFunction(object sender, DoWorkEventArgs e)
{
[...]
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
[...]
Clipboard.SetText(splitpermutation[i]);
[...]
}
}
You could marshal this to the UI thread to make it work:
else
{
[...]
this.Dispatcher.BeginInvoke(new Action(() => Clipboard.SetText(splitpermutation[i])));
[...]
}
The BackgroundWorker runs on the .NET thread pool. Thread pool threads run in the COM multi-threaded apartment. To use the clipboard, you must be running in a single-threaded apartment. You could create your own thread and set it to run in an STA, but it would probably be best to use Control.Invoke (or BeginInvoke) to get back onto a user-interface thread (which must be an STA thread).
The exception you're getting is because you're trying to do something on the UI thread from outside the UI thread (a BackgroundWorker, as the name implies, does something in the background, and for that it needs to create a separate thread).
While the answer posted by Reed (that is, by using Dispatcher.BeginInvoke) is one way to do avoid this exception, I'm wondering WHY you are trying to send something to the clipboard from the main work method in the first place...
The BackgroundWorker exposes events like ProgressChanged (which you can call periodically from your work method) or RunWorkerCompleted (which will fire when the main work method finishes).
Using Clipboard.SetText in either of these events should not cause the exception you're seeing, and this seems to be the preferable way of doing things on the UI thread when working with the BackgroundWorker.
Suppose I have a routine like this:
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(Some_Work));
t.Start();
}
I need to put a condition so that, "If there is not already a thread running apart from the Main thread, start a new thread".
But how to test for whether a thread is running other than the Main thread?
There's several other threads running in your .NET app before it even gets to button1_Click. For instance, the finalizer thread is always hanging around, and I think WinForms creates one or two for itself.
Do you need to know about other threads that you've created? If so, you should keep track of them yourself. If not: why?
Edit: are you looking to kick off some background processing on demand? Have you seen the BackgroundWorker component, which manages threads for you, together with their interaction with the user interface? Or direct programming against the thread pool, using Delegate.BeginInvoke or ThreadPool.QueueUserWorkItem?
The easiest solution is of course to either store a reference to the existing thread, or just set a flag that you have a thread running.
You should also maintain that field (reference or flag) so that if the thread exits, it should unset that field so that the next "start request" starts a new one.
Easiest solution:
private volatile Thread _Thread;
...
if (_Thread == null)
{
_Thread = new Thread(new ThreadStart(Some_Work));
_Thread.Start();
}
private void Some_Work()
{
try
{
// your thread code here
}
finally
{
_Thread = null;
}
}
I am developing a application in C# which is getting data from Serial Port, Processing it and showing it to UI.
The data is coming very fast between 5-50ms speed. Before I was not using any threads. and so application was relying on single App thread which was getting data from Serial Port, Processing data and showing it to UI. and It was loosing some data.
Then I started implementing BackgroundWorker thread to remove some overhead on single thread and thinking of good performance. And Now I am getting "This BackgroundWorker is currently busy and cannot run multiple tasks concurrently" error. I think Thread is not able to cope up with the speed the data coming from Serial port. and So throwing error on executing "backgroundWorker1.RunWorkerAsync(data);". I need some suggestions what's the better approach to implement such kind of scenario?
geofftnz is correct, and I'll expand a bit for you. You should only start the background worker once, and have it feed the data back to the GUI thread using ReportProgress. worker thread would look something like this.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackGroundWorker;
while(!e.CancellationPending)
{
ResultObject ro = new ResultObject(); // your own type here, obviously
//Process and store some data in ro;
worker.ReportProgress(0, ro);
//Do not modify ro after reporting progress to avoid threading issues
}
}
From the GUI, register to the ProgressChanged event, and only start the worker one time.
The problem is that you're calling backgroundWorker1.RunWorkerAsync() before it's finished with its previous operation.
What you probably want to have is a single thread reading the serial port, buffering the data and notifying the main UI thread that data is available.
Try adding this in, to make sure the background worker is only running one job at a time.
if(!backgroundWorker1.IsBusy)
backgroundWorker1.RunWorkerAysnc();
You also have the ability to cancel the current job, here is a code sample
private void WhereBackworkerStarts()
{
backgroundWorker.WorkerSupportsCancellation = true;
if (backgroundWorker.IsBusy)
backgroundWorker.CancelAsync();
else
backgroundWorker.RunWorkerAsync();
}
// Events
static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for(int i = 0; i < int.MaxValue; i++)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
return;
}
// Do work here
}
e.Result = MyResult;
}
static void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Deal with results
}