I'm trying to use background worker to update a text label continuously, but for DoWork, if no loop used, it will only execute once, but if infinite loop is used, it freezes the GUI, any idea to solve this? Really appreciate!!! I'm pretty new to C# and still trying to learn.
Here's my code:
This in the main form:
backgroundWorkerX.DoWork += backgroundWorkerX_DoWork;
backgroundWorkerX.ProgressChanged += backgroundWorkerX_ProgressChanged;
backgroundWorkerX.WorkerReportsProgress = true;
backgroundWorkerX.RunWorkerAsync();
Then:
public void backgroundWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
}
public void backgroundWorkerX_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label9.Text = e.ProgressPercentage.ToString();
}
public void backgroundWorkerX_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
[...] if no loop used, it will only execute once
This is the expected behavior. It will do the thing it's expected and then call RunWorkerCompleted.
[...] but if infinite loop is used, it freezes the GUI.
You don't present a loop in your question. So I'll make an assumption and will give you an educated guess that it actually consumes all the CPU, and therefore crashes the GUI.
// I assume that you loop like this somehow.
do
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
} while (true);
Try to add something to "slow it down" once in a while. Otherwise it will just run and run as fast as possible, and therefore consume all the CPU available. Remember that everytime you use ReportProgress it will be rendered by the GUI-thread. And this will execute very often and not leave a lot of resources for the GUI thread to respond to other things. Below I use Thread.Sleep, which will pause the thread for half a second. But remember that you will only want to do this in a background thread.
do
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
Thread.Sleep(500);
} while (true);
I would also consider doing this another way. If you actually have the need of pausing the thread, you might as well do it with a Timer. Thread.Sleep will lock the thread, and may therefore not be what you really want. From this answer:
Process() // method to be called after regular interval in Timer
{
// lengthy process, i.e. data fetching and processing etc.
// here comes the UI update part
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
}
Related
I can't stop my app because I have a while loop, so the gui doesn't let me click the stop button, it looks something like:
private void btnStart_Click(object sender, EventArgs e)
{ while(true)
{
//some code here
}
}
//some methods here
private void btnStop_Click(object sender, EventArgs e)
{
Application.Exit();
}
Firstly if you're running some sort of long running background job then you should not be doing that in the UI thread, because as you state, you cannot then do anything else in the UI thread to stop the loop. You'll need to defer that work to a secondary thread, or a timer, or something outside of the context of the UI thread. You can Google many different ways to achieve this.
Secondly, if you need to stop the loop then change the while condition for the loop from true to some other monitorable condition, e.g. a variable called keepRunning, which you can then set from within the btnStop_Click method. Of course if you adopt my advice in the first point then there may be some other way to stop the loop from running, e.g. if it's a timer then you can stop that timer in the appropriate way.
Thirdly, you should wait for the background operation to stop before closing the application.
I have a function which I need to run in background because it freezes the UI until it completes. I tried to use Async/Await which lets me use the UI no matter the function completes running or not, but I noticed it is much slower. Why using async/await to a function takes longer time then calling that same function directly ? Is there any other alternative ?
private void btnClick(object sender, EventArgs e)
{
Math4OfficeRibbon.CallFunction();
MessageBox.Show("Task Finished");
}
public async void CallFunction()
{
await Task.Run(() => AwaitedFunction());
}
public static void AwaitedFunction()
{
// Do Something
// Takes longer time this way
}
In order to find out why it's much slower you can track events down in visual studio by using Console.WriteLine($"{event name} {DateTime.Now}")
And then seeing where it takes the most time in output window.
However about the alternatives, I suggest you use BackgroundWorker to run your tasks.
note that you need to invoke controls in order to make changes to the ui through the backgroundWorker
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork+=(o,args)=>
{
//your code here.
}
_worker.RunWorkerAsync();
You also have RunWorkerCompleted event which you can use to do things after your task is done running.
Backgroundworker also has the IsBusy property which you can use along with a while loop to keep the thread waiting for its completion without freezing the UI by doing :
While(_worker.IsBusy)
{
Application.DoEvents();
}
In order to invoke to do things on the ui thread you need to do the following within BackgroundWorker:
BeginInvoke(new Action(()=>
{
//ui action here for example:
MessageBox.show("test")
}));
However in order to find out why your asynchronous operation takes alot of time you have to trace it using the console because you have all the code and you know what you're doing.
I am trying to make my foreach loop asynchronous. The loop loops the first time, does something then it will wait 40 seconds and then the loop loops again does something, waits 40 seconds etc...
In my situation I am looping around tweets, getting the tweet user name and then sending a tweet to them.
I tried using the Thread.Sleep(40000) method but that just freezes the app and I can't use it, and the loop doesn't stop for 40 seconds and then loops again. The app freezes but the loop doesn't, the tweets are getting sent without even waiting at all. This how my code looks like
private void tweetButton_Click(object sender, EventArgs e)
{
var tweets= .........
foreach (var tweet in tweets)
{
// Do Something
System.Threading.Thread.Sleep(40000);
}
How would I let the foreach loop to pause before looping again on its self, and how would I make it pause without freezing my application?
https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx
I think you can await Task.Delay(40000); - to me that would be an 'async sleep' like you've asked for.
Alternatively, you could kick off a timer. The timer is probably a better choice, but will run on a threadpool thread.
EDIT: Not sure why this got -1...but if anyone has an explanation, I'd be interested.
I've verified that 'await Task.Delay(xyz)' does work for this purpose. Naturally, you'd need the async keyword to be added.
static async void DoSomething()
{
for(var i = 0;i<25;i++)
{
// Do Something
await Task.Delay(1000);
Console.WriteLine("Sending a Tweet");
}
}
I can't speak to the performance, but it seems to work fine for me.
You want your code to run on a different thread, so it doens note impede the code running in your winforms gui thread.
The best way for you specific needs is a timer that you kickoff once and that then does your job.
{
Timer t = new Timer();
t.Interval = 50000;
t.Elapsed += T_Elapsed;
t.Start();
}
private static void T_Elapsed(object sender, ElapsedEventArgs e)
{
}
If you need to access your winforms controls from here, you can use invoke, so the commands are placed into the the gui threads message loop.
control.Invoke((MethodInvoker)(() =>
{
//Do your thing
}));
You could implement background process in form. That would works in your case.
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.
I'm trying to make my C# application multi threaded because sometimes, I get an exception that says I have made a call to a thread in an unsafe manner. I've never done any multi-threading before in a program, so bear with me if I sound kinda ignorant on the issue.
The overview of my program is that I want to make a performance monitoring applicaiton. What this entails is using the process and performance counter class in C# to launch and monitor an application's processor time, and sending that number back to the UI. However, in the method that actually calls the performance counter's nextValue method (which is set to perform every second thanks to a timer), I would sometimes get the aforementioned exception that would talk about calling a thread in an unsafe manner.
I've attached some of the code for your perusal. I know this is kind of a time consuming question, so I'd be really grateful if anyone could offer me any help as to where to make a new thread and how to call it in a safe way. I tried looking at what was up on MSDN, but that just kinda confused me.
private void runBtn_Click(object sender, EventArgs e)
{
// this is called when the user tells the program to launch the desired program and
// monitor it's CPU usage.
// sets up the process and performance counter
m.runAndMonitorApplication();
// Create a new timer that runs every second, and gets CPU readings.
crntTimer = new System.Timers.Timer();
crntTimer.Interval = 1000;
crntTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
crntTimer.Enabled = true;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
// get the current processor time reading
float cpuReading = m.getCPUValue();
// update the current cpu label
crntreadingslbl.Text = cpuReading.ToString(); //
}
// runs the application
public void runAndMonitorApplication()
{
p = new Process();
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = fileName;
p.Start();
pc = new System.Diagnostics.PerformanceCounter("Process",
"% Processor Time",
p.ProcessName,
true);
}
// This returns the current percentage of CPU utilization for the process
public float getCPUValue()
{
float usage = pc.NextValue();
return usage;
}
Check out Jon Skeet's article on multi-threading, particularly the page on multi-threading winforms. It should fix you right up.
Basically you need to check to see if an invoke is required, and then perform the invoke if needed. After reading the article you should be able to refactor your UI-updating code into blocks that look like this:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
// get the current processor time reading
float cpuReading = m.getCPUValue();
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new Action(() => crntreadingslbl.Text = cpuReading.ToString()));
return;
}
// Must be on the UI thread if we've got this far
crntreadingslbl.Text = cpuReading.ToString();
}
In your code, an invoke will be required because you are using a Timer. According to the documentation for System.Timers.Timer:
The Elapsed event is raised on a ThreadPool thread.
This means that the OnTimedEvent() method that you set as the Timer's delegate will execute on the next available ThreadPool thread, which will definitely not be your UI thread. The documentation also suggests an alternate way to solve this problem:
If you use the Timer with a user
interface element, such as a form or
control, assign the form or control
that contains the Timer to the
SynchronizingObject property, so that
the event is marshaled to the user
interface thread.
You may find this route easier, but I haven't tried it.
Your problem, I think, is that this line:
crntreadingslbl.Text = cpuReading.ToString();
Is running outside of the UI thread. You cannot update a UI element outside of the UI thread. You need to call Invoke on the Window to call a new method on the UI thread.
All that said, why not use perfmon? It's built for purpose.
The BackGroundWorker component may help you. It is available on the toolbox so you can drag to your form.
This component exposes a set of events to execute tasks in a thread different than the UI thread. You don't have to worry about creating a thread.
All the interaction between the code running on background and the UI controls must be done via the event handlers.
For your scenario you can setup a timer to trigger the background worker at a specific interval.
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Then you implement the proper event handlers to actually collect data and update the UI
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Collect performance data and update the UI
}