I have c# application which having 2 buttons. First having for loop which is run 10k times. and each loop code execution take 1 second to finish.
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
}
some time i want to stop this loop/ execution on click on another button "Stop", but its not working. Please suggest me what solution.
It's not good practice to run long running operations in UI Thread (thread where all UI events are handled - such as button click). You should run your loop in another that.
You can use Task Parallel Library (TPL):
private bool stopIt = false;
private void Form1_Load(object sender, EventArgs e)
{
Task task = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 1000000000; i++)
{
if (!stopIt)
{
Console.WriteLine("Here is " + i);
}
else
{
break;
}
}
});
}
private void button1_Click(object sender, EventArgs e)
{
stopIt = true;
}
The simplest solution (not best) is to add Application.DoEvents() into the loop to process button events:
private bool cancel;
public void loop()
{
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
Application.DoEvents();
if (cancel)
break;
}
}
public void cancelButton_Click(object sender, EventArgs e)
{
cancel=true;
}
Much better and still simple solution is to employ async Task (the rest of the code stays the same minus Application.DoEvents() call):
private void loopButton_Click(object sender, EventArgs e)
{
new Task(loop).Start();
}
Beware that you should use this.Invoke(new Action(() => { <your code> } )); to access UI controls from the loop in this case.
Related
Is there a simple way to have elements on a form keep updating even after I click on Windows Show Desktop? The following code updates the value in textBox1 until I click on Windows Show Desktop (Windows 10 - click on the bottom right of the screen). I prefer not to use Application.DoEvents().
void Button1Click(object sender, EventArgs e)
{
int n = 0;
while(true) {
textBox1.Text = n++.ToString();
textBox1.Refresh();
Update();
// Application.DoEvents();
Thread.Sleep(200);
}
}
Using Thread.Sleep blocks the current thread (UI thread); you can fix it like this:
private async void button1_Click(object sender, EventArgs e)
{
int n = 0;
while (true)
{
textBox1.Text = n++.ToString();
await Task.Delay(200);
}
}
If I add a button to the window and handle its click async event with a long process, it is possible to click multiple times a button even it is in progress. It keeps the events in the queue and executes them sequentially.
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Delay(2000);
Console.WriteLine("Button 1 ends");
}
The question: Is it possible to get like that result in sync form of event?
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10000000; i++)
{
}
Console.WriteLine("Button 1 ends");
}
We want to prevent multiple clicks when a user tries. If the system frozen or something went wrong, the user is able to click multiple times but we want to prevent this.
Edit: sorry I initially misunderstood the question thinking the requirement was to queue the clicks and prevent multiple simultaneous executions. Here is an update
private async void Button_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
button.IsEnabled = false;
try
{
// Perform long task here
}
finally
{
button.IsEnabled = true;
}
}
Why don't you use a flag inside the semaphore?
// Initialize the Semaphore to initial and max count of 1
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private bool alreadyExecuted = false;
private async void Button_Click(object sender, RoutedEventArgs e)
{
var tmp = await functionToCall();
}
private async Task<bool> functionToCall()
{
// Wait for semaphone to have an available slot
await _semaphore.WaitAsync();
try
{
if (!alreadyExecuted)
{
// Perform long task here
// Only one handler can be here at a time. You can increase this by increasing max count and initial count of the semaphore in the constructor.
}
}
finally
{
alreadyExecuted = true;
// Release the lock in a finally block to ensure it is released even in case of an error
_semaphore.Release();
}
return alreadyExecuted;
}
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();
}
I have c# application which having 2 buttons. First having for loop which is run 10k times. and each loop code execution take 1 second to finish.
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
}
some time i want to stop this loop/ execution on click on another button "Stop", but its not working. Please suggest me what solution.
It's not good practice to run long running operations in UI Thread (thread where all UI events are handled - such as button click). You should run your loop in another that.
You can use Task Parallel Library (TPL):
private bool stopIt = false;
private void Form1_Load(object sender, EventArgs e)
{
Task task = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 1000000000; i++)
{
if (!stopIt)
{
Console.WriteLine("Here is " + i);
}
else
{
break;
}
}
});
}
private void button1_Click(object sender, EventArgs e)
{
stopIt = true;
}
The simplest solution (not best) is to add Application.DoEvents() into the loop to process button events:
private bool cancel;
public void loop()
{
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
Application.DoEvents();
if (cancel)
break;
}
}
public void cancelButton_Click(object sender, EventArgs e)
{
cancel=true;
}
Much better and still simple solution is to employ async Task (the rest of the code stays the same minus Application.DoEvents() call):
private void loopButton_Click(object sender, EventArgs e)
{
new Task(loop).Start();
}
Beware that you should use this.Invoke(new Action(() => { <your code> } )); to access UI controls from the loop in this case.
I have a loop that I would like to stop using a button.
Edited for better understanding:
I do realize that you cannot stop a button while a loop was running since it will not work as long as that current UI is running. What I'm really asking for is the most efficient way of creating a thread or using BGWorker to stop this. I have seen some methods, but most of them are for Java and not C#.
What I would like to do is:
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums; i++)
{
doSomething();
}
}
private void stop_Click(object sender, EventArgs e)
{
stops start_Click()
}
You can't do that. For starters, the for loop is running synchronously on the UI thread, which means you won't even be able to click the "Stop" button.
Hence, you need to move the operations of the for loop onto another thread, which means you likely won't be using a for loop at all. You need to think about how the code inside actually needs to be executed, then based on how you are doing the processing, you can implement the "Stop" button.
A very simple way to do this would be to just:
new Thread(() =>
{
int i = 0;
while (!stop && i < num)
{
doSomething();
i++;
}
}).Start();
And set stop to stop the processing loop. In a more realistic scenario, you could queue up functions that you want to process, then stop dequeuing via a similar method. Unfortunately, its hard to reccommend a setup without knowing more details.
Any solution based on your code will also have the problem of the current doSomething() completing execution (which could take a while). Again, without more info, its hard to say what the best approach to fixing that is.
To keep your UI responsive to be able to cancel the running operation you can use a backgrounworker.
The backgroundworker does the work in an other thread while keeping your UI responsive:
private readonly BackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
_backgroundWorker.DoWork += backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
Disposed += Form1_Disposed;
}
private void Form1_Disposed(object sender, EventArgs e)
{
_backgroundWorker.Dispose();
}
private void StartLoop()
{
if ( !_backgroundWorker.IsBusy )
{
_backgroundWorker.RunWorkerAsync();
}
}
private void StopLoop()
{
_backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork( object sender , DoWorkEventArgs e )
{
var backgroundWorker = ( BackgroundWorker ) sender;
for ( var i = 0; i < 100; i++ )
{
if ( backgroundWorker.CancellationPending )
{
e.Cancel = true;
return;
}
// Do Work
}
}
private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e )
{
if ( e.Cancelled )
{
// handle cancellation
}
if ( e.Error != null )
{
// handle error
}
// completed without cancellation or exception
}
IMHO, it's likely the best approach here is to convert your work to an asynchronous operation and then use the async/await idiom for the loop. E.g.:
private bool _stopLoop;
private async void start_Click(object sender, EventArgs e)
{
_stopLoop = false;
for(int i = 0; i < nums && !_stopLoop; i++)
{
await Task.Run(() => doSomething());
}
}
private void stop_Click(object sender, EventArgs e)
{
_stopLoop = true;
}
This allows the loop itself to execute in the UI thread where the _stopLoop variable is being managed, but without actually blocking the UI thread (which among other things would prevent the "Stop" button from being clicked).
Unfortunately, you didn't provide details about how doSomething() works. It's possible there's a good way to convert that whole method to be an async method, but I can't comment on that without the actual code.
Note that this approach will only interrupt the loop at a point in between each operation. If you want to be able to interrupt the doSomthing() operation itself, you'll have to provide a mechanism for that. One likely approach would be to use CancellationSource and CancellationToken, which provides a convenient way to express cancellation semantics.
Try using an async/await approach. It's quite easy!
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
private CancellationTokenSource _tokenSource;
private async void start_Click(object sender, EventArgs e)
{
if (_tokenSource != null)
return;
_tokenSource = new CancellationTokenSource();
var ct = _tokenSource.Token;
await Task.Factory.StartNew(() =>
{
for (; ; )
{
if (ct.IsCancellationRequested)
break;
doSomething();
}
}, ct);
_tokenSource = null;
}
private int _labelCounter;
private void doSomething()
{
// do something
Invoke((Action)(() =>
{
myLabel.Text = (++_labelCounter).ToString();
}));
}
private void stop_Click(object sender, EventArgs e)
{
if (_tokenSource == null)
return;
_tokenSource.Cancel();
}
}
try this :
bool stop=false;
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums&& !bool; i++)
{
doSomething();
}
}
and in the click event
set
stop=true;