I am currently somewhat new to c#/wpf (and coding in general). I decided to start another project, being a custom made "task manager" of sorts.
(While I use binding, this is NOT a MVVM project, so all answers welcome)
If you have ever opened task manager, you know that one of the main helpful tools it provides is a updating view of CPU/RAM/Whatever usage. Telling the user what percent of the resource they are using.
My problem is not getting the CPU percentage. I am unsure on how to refresh the text property for CPU load in the UI efficiently.
My first thought was that I should create a Background worker (which is probably correct) to separate the thread loads. However, I can't seem to wrap my mind on the solution to implement the background workers in a useful way.
The code is currently set up in this fashion:
When page is loaded, public BgWrk creates a new instance of it self.
Adds task to be called when ran.
BgWrk is ran.
New instance of method to be called is made.
Dispatcher is invoked on main thread to update UI.
Invoke consists of setting public string PerCpu (bound in other class, using INotifyPropertyChanged & all) on the return value of "grabber"'s CpuPerUsed.
BgWrk disposed.
Program loops (this is most likely the problem).
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
BgWrk = new BackgroundWorker();
BgWrk.DoWork += new DoWorkEventHandler(BackgroundWorker1_DoWork);
BgWrk.RunWorkerAsync();
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
CpuInfoGrabber grabber = new CpuInfoGrabber();
Application.Current.Dispatcher.Invoke(new Action (() => Bnd.PerCpu = grabber.CpuPerUsed()));
BgWrk.Dispose();
}
}
Again the code works, but it is WAY to slow due to the load of retrieving all of that data. Any suggestions on how to make this work well are appreciated!
Thanks
Instead of looping you could use a timer to periodically poll for the CPU usage.
class Test
{
private System.Timers.Timer _timer;
public Test( )
{
_timer = new System.Timers.Timer
{
// Interval set to 1 millisecond.
Interval = 1,
AutoReset = true,
};
_timer.Elapsed += _timer_Elapsed;
_timer.Enabled = true;
_timer.Start( );
}
private void _timer_Elapsed( object sender, System.Timers.ElapsedEventArgs e )
{
// This handler is not executed on the gui thread so
// you'll have to marshal the call to the gui thread
// and then update your property.
var grabber = new CpuInfoGrabber();
var data = grabber.CpuPerUsed();
Application.Current.Dispatcher.Invoke( ( ) => Bnd.PerCpu = data );
}
}
I'd use Task.Run instead of a BackgroundWorker in your case:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
//Keep it running for 5 minutes
CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(hours: 0, minutes: 5, seconds: 0));
//Keep it running until user closes the app
//CancellationTokenSource cts = new CancellationTokenSource();
//Go to a different thread
Task.Run(() =>
{
//Some dummy variable
long millisecondsSlept = 0;
//Make sure cancellation not requested
while (!cts.Token.IsCancellationRequested)
{
//Some heavy operation here
Thread.Sleep(500);
millisecondsSlept += 500;
//Update UI with the results of the heavy operation
Application.Current.Dispatcher.Invoke(() => txtCpu.Text = millisecondsSlept.ToString());
}
}, cts.Token);
}
Related
I've looked at some guides and none of them have gotten me all the way there. I've never made a thread, discussed a thread, or seen a thread at the grocery store, so this may be a problem. Currently. I'm trying:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
}
public void dothis()
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
}
That gets me InvalidOperationException was unhandled on the
lblTimerHour.Text = intHour.ToString("00");
line. I read what that means and... it might as well be in Mandarin, I kind of get the general concept-ish of what's going wrong, but it's painfully fuzzy. If you asked me the first step in fixing it I'd look at you like a deer in the headlights. We just haven't gotten that far in my class yet.
The problem here is that the label you are trying to update is owned by the main thread (i.e. what the UI runs on), and that means that only that thread can access/update it. So, since you are in a different thread, you need to tell the UI thread to update the label for you.
Something like this would work:
Action updateLabel = () => lblTimerHour.Text = intHour.ToString("00");
lblTimerHour.BeginInvoke(updateLabel);
What this does is tell the lblTimerHour to invoke the action you define above (updateLabel).
See this post: How to update the GUI from another thread in C#?
lblTimerHour.Invoke((MethodInvoker)delegate {
//Do what you need to do with the label
lblTimerHour.Text = intHour.ToString("00");
});
Edit
This should do the trick:
public void dothis()
{
do
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Invoke((MethodInvoker)delegate {
//Update the label from the GUI thread
lblTimerHour.Text = intHour.ToString("00");
});
//Pause 1 sec. Won't freeze the gui since it's in another thread
System.Thread.Sleep(1000);
}while(true); //Thread is killed on mouse up
}
Well, let's take a look and see what you already have.
First, I see you did this.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
While this certainly is not the freshest stuff around it will still work. If you wanted some fresher ingredients then you might go with this instead.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
Task.Factory.StartNew(dothis);
}
Second, I see this.
public void dothis()
{
if (intHour < 23) intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
The problem here is that you are attempting to update a UI control from a thread other than the main UI thread. You see UI controls have what is called thread affinity. They can only ever be accessed from the thread that created them. What you have will lead to all kinds of unpredictable problems up to and including tearing a whole in spacetime.
A better option would be to do this.
public void dothis()
{
while (intHour < 23)
{
intHour = intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
}
}
I assumed that you were missing the loop so I added it. While I cannot say that I personally have a taste for this kind of thing it is much easier to swallow. The real problem here is that the worker thread really does not do a whole lot of useful work. And then to top it off we have to use an awkward marshaling operation to transfer the result back to the UI thread. It is not pretty, but it will work.
And finally that brings me to this.
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
You are attempting to abort a thread which is highly inadvisable. The problem is that it yanks control from the thread at unpredictable times. That thread might be in the middle of a write to data structure which would corrupt it. This is actually a pretty bad problem because any data structure in the process of being manipulated from any one of the frames on the call stack could be in an inconsistent state. This includes code you did not write. That is why it is hard to say what you may or may not be corrupting by doing this.
What you need to consider instead is using the cooperative cancellation mechanisms. This includes the use of CancellationTokenSource and CancellationToken. Here is how it might look once we put everything together.
private CancellationTokenSource cts = null;
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
cts = new CancellationTokenSource();
Task.Factory.StartNew(() => dothis(cts.Token));
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
cts.Cancel();
}
public void dothis(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
Thread.Sleep(1000);
}
}
What this does is signal that the worker thread should gracefully shutdown on its own. This gives the worker thread a chance to tidy things up before eventually terminating itself.
If you want to update the UI every X period of time then there are already existing tools for this; a Timer will do exactly what you want, and it will be much more efficient and easier to code than creating a new thread that just spends most of its time napping. Additionally, aborting threads is a very bad sign to see. Avoid it at all costs.
First create the timer and configure it in the constructor:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private int hour = 0;
private int step = 0;
public Form1()
{
InitializeComponent();
timer.Tick += timer_Tick;
timer.Interval = 1000;
}
Have the Tick event do whatever should be done whenever it ticks.
private void timer_Tick(object sender, EventArgs e)
{
if (hour < 23)
{
hour += step;
lblTimerHour.Text = hour.ToString("00");
}
}
Then just start the timer when you want it to start ticking and stop the timer when you want it to stop:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
timer.Start();
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
timer.Stop();
}
The timer will automatically ensure that the Tick event handler runs in the UI thread, and it won't block the UI thread (or any other thread) when its waiting for the next event to happen, it will just do nothing.
I have a WinForms application that consists of a main UI thread and 4 tasks. My main form has a private member level variable like this:
private bool keepThreadsRunning = false;
In the Load() event of my main form, I have the following:
keepThreadsRunning = true;
var task1Worker = Task.Factory.StartNew(() => DoStuff1());
var task2Worker = Task.Factory.StartNew(() => DoStuff2());
var task3Worker = Task.Factory.StartNew(() => DoStuff3());
var task4Worker = Task.Factory.StartNew(() => DoStuff4());
Inside of each of my DoStuff() methods, I basically have this:
while (keepThreadsRunning)
{
// do work here
Thread.Sleep(30000); // a couple of my tasks only need to run every 30 seconds or so
}
Lastly, in my Form_Closing() event handler, I have the following:
keepThreadsRunning = false;
this.Close();
Watching my application in task manager, it appears that the process is ending when I close my form but I'm a little confused about the four tasks. Is my call to this.Close() really causing those tasks to terminate (even if they're in the Thread.Sleep() call when it happens)? And is there a better way of accomplishing this than the way I'm coding it right now?
EDIT - I've looked briefly at task cancellation (when my app exits) but my understanding is that my tasks would need to periodically check the cancellation token to determine if they've been cancelled. Given that some of my tasks need to run every 30 seconds, I couldn't figure out how I'd implement that 30s wait (currently a Thread.Sleep()) and still have the task be checking the cancellation token.
Rather than using a boolean and Thread.Sleep(), use a WaitHandle, specifically a ManualResetEvent, created like this:
var threadTerminationHandle = new ManualResetEvent(false);
In your thread:
do {
// do work here
} while (!threadTerminationHandle.WaitOne(TimeSpan.FromSeconds(30))
This will wait until the WaitHandle is set, or 30 seconds elapses, whichever is sooner.
In your form:
threadTerminationHandle.Set();
Close();
First of all, closing the main UI thread will terminate your other tasks. If you need them to keep running, maybe consider running them in a seperate Console Application, or a Windows Service.
Even if you found a way to delay the closing of the form while you finish running the methods you need to run, this would only work if the end user closed the form in the way you wanted, and Windows being Windows there are a million and one ways to close an application so there is no guarantee that this will work.
For running a method asynchronously every x amount of seconds, you could just use a timer for the whole thing, like so:
using System;
using System.Timers;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var timer1 = new System.Timers.Timer { Interval = 30000, Enabled = true };
var timer2 = new System.Timers.Timer { Interval = 20000, Enabled = true };
var timer3 = new System.Timers.Timer { Interval = 10000, Enabled = true };
var timer4 = new System.Timers.Timer { Interval = 5000, Enabled = true };
timer1.Elapsed += timer1_Elapsed;
timer2.Elapsed += timer2_Elapsed;
timer3.Elapsed += timer3_Elapsed;
timer4.Elapsed += timer4_Elapsed;
}
void timer4_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
void timer3_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
void timer2_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
//do work here
}
}
}
When you close application, tasks will be closed accordingly because task is processed under background thread from thread pool. So, you don't need to periodically check the cancellation token to determine if they've been cancelled
I am currently writing my first program on C# and I am extremely new to the language (used to only work with C so far). I have done a lot of research, but all answers were too general and I simply couldn't get it t work.
So here my (very common) problem:
I have a WPF application which takes inputs from a few textboxes filled by the user and then uses that to do a lot of calculations with them. They should take around 2-3 minutes, so I would like to update a progress bar and a textblock telling me what the current status is.
Also I need to store the UI inputs from the user and give them to the thread, so I have a third class, which I use to create an object and would like to pass this object to the background thread.
Obviously I would run the calculations in another thread, so the UI doesn't freeze, but I don't know how to update the UI, since all the calculation methods are part of another class.
After a lot of reasearch I think the best method to go with would be using dispatchers and TPL and not a backgroundworker, but honestly I am not sure how they work and after around 20 hours of trial and error with other answers, I decided to ask a question myself.
Here a very simple structure of my program:
public partial class MainWindow : Window
{
public MainWindow()
{
Initialize Component();
}
private void startCalc(object sender, RoutedEventArgs e)
{
inputValues input = new inputValues();
calcClass calculations = new calcClass();
try
{
input.pota = Convert.ToDouble(aVar.Text);
input.potb = Convert.ToDouble(bVar.Text);
input.potc = Convert.ToDouble(cVar.Text);
input.potd = Convert.ToDouble(dVar.Text);
input.potf = Convert.ToDouble(fVar.Text);
input.potA = Convert.ToDouble(AVar.Text);
input.potB = Convert.ToDouble(BVar.Text);
input.initStart = Convert.ToDouble(initStart.Text);
input.initEnd = Convert.ToDouble(initEnd.Text);
input.inita = Convert.ToDouble(inita.Text);
input.initb = Convert.ToDouble(initb.Text);
input.initc = Convert.ToDouble(initb.Text);
}
catch
{
MessageBox.Show("Some input values are not of the expected Type.", "Wrong Input", MessageBoxButton.OK, MessageBoxImage.Error);
}
Thread calcthread = new Thread(new ParameterizedThreadStart(calculations.testMethod);
calcthread.Start(input);
}
public class inputValues
{
public double pota, potb, potc, potd, potf, potA, potB;
public double initStart, initEnd, inita, initb, initc;
}
public class calcClass
{
public void testmethod(inputValues input)
{
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
int i;
//the input object will be used somehow, but that doesn't matter for my problem
for (i = 0; i < 1000; i++)
{
Thread.Sleep(10);
}
}
}
I would be very grateful if someone had a simple explanation how to update the UI from inside the testmethod. Since I am new to C# and object oriented programming, too complicated answers I will very likely not understand, I'll do my best though.
Also if someone has a better idea in general (maybe using backgroundworker or anything else) I am open to see it.
First you need to use Dispatcher.Invoke to change the UI from another thread and to do that from another class, you can use events.
Then you can register to that event(s) in the main class and Dispatch the changes to the UI and in the calculation class you throw the event when you want to notify the UI:
class MainWindow : Window
{
private void startCalc()
{
//your code
CalcClass calc = new CalcClass();
calc.ProgressUpdate += (s, e) => {
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
};
Thread calcthread = new Thread(new ParameterizedThreadStart(calc.testMethod));
calcthread.Start(input);
}
}
class CalcClass
{
public event EventHandler ProgressUpdate;
public void testMethod(object input)
{
//part 1
if(ProgressUpdate != null)
ProgressUpdate(this, new YourEventArgs(status));
//part 2
}
}
UPDATE:
As it seems this is still an often visited question and answer I want to update this answer with how I would do it now (with .NET 4.5) - this is a little longer as I will show some different possibilities:
class MainWindow : Window
{
Task calcTask = null;
void buttonStartCalc_Clicked(object sender, EventArgs e) { StartCalc(); } // #1
async void buttonDoCalc_Clicked(object sender, EventArgs e) // #2
{
await CalcAsync(); // #2
}
void StartCalc()
{
var calc = PrepareCalc();
calcTask = Task.Run(() => calc.TestMethod(input)); // #3
}
Task CalcAsync()
{
var calc = PrepareCalc();
return Task.Run(() => calc.TestMethod(input)); // #4
}
CalcClass PrepareCalc()
{
//your code
var calc = new CalcClass();
calc.ProgressUpdate += (s, e) => Dispatcher.Invoke((Action)delegate()
{
// update UI
});
return calc;
}
}
class CalcClass
{
public event EventHandler<EventArgs<YourStatus>> ProgressUpdate; // #5
public TestMethod(InputValues input)
{
//part 1
ProgressUpdate.Raise(this, status); // #6 - status is of type YourStatus
// alternative version to the extension for C# 6+:
ProgressUpdate?.Invoke(this, new EventArgs<YourStatus>(status));
//part 2
}
}
static class EventExtensions
{
public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent,
object sender, T args)
{
if (theEvent != null)
theEvent(sender, new EventArgs<T>(args));
}
}
#1) How to start the "synchronous" calculations and run them in the background
#2) How to start it "asynchronous" and "await it": Here the calculation is executed and completed before the method returns, but because of the async/await the UI is not blocked (BTW: such event handlers are the only valid usages of async void as the event handler must return void - use async Task in all other cases)
#3) Instead of a new Thread we now use a Task. To later be able to check its (successfull) completion we save it in the global calcTask member. In the background this also starts a new thread and runs the action there, but it is much easier to handle and has some other benefits.
#4) Here we also start the action, but this time we return the task, so the "async event handler" can "await it". We could also create async Task CalcAsync() and then await Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false); (FYI: the ConfigureAwait(false) is to avoid deadlocks, you should read up on this if you use async/await as it would be to much to explain here) which would result in the same workflow, but as the Task.Run is the only "awaitable operation" and is the last one we can simply return the task and save one context switch, which saves some execution time.
#5) Here I now use a "strongly typed generic event" so we can pass and receive our "status object" easily
#6) Here I use the extension defined below, which (aside from ease of use) solve the possible race condition in the old example. There it could have happened that the event got null after the if-check, but before the call if the event handler was removed in another thread at just that moment. This can't happen here, as the extensions gets a "copy" of the event delegate and in the same situation the handler is still registered inside the Raise method.
I am going to throw you a curve ball here. If I have said it once I have said it a hundred times. Marshaling operations like Invoke or BeginInvoke are not always the best methods for updating the UI with worker thread progress.
In this case it usually works better to have the worker thread publish its progress information to a shared data structure that the UI thread then polls at regular intervals. This has several advantages.
It breaks the tight coupling between the UI and worker thread that Invoke imposes.
The UI thread gets to dictate when the UI controls get updated...the way it should be anyway when you really think about it.
There is no risk of overrunning the UI message queue as would be the case if BeginInvoke were used from the worker thread.
The worker thread does not have to wait for a response from the UI thread as would be the case with Invoke.
You get more throughput on both the UI and worker threads.
Invoke and BeginInvoke are expensive operations.
So in your calcClass create a data structure that will hold the progress information.
public class calcClass
{
private double percentComplete = 0;
public double PercentComplete
{
get
{
// Do a thread-safe read here.
return Interlocked.CompareExchange(ref percentComplete, 0, 0);
}
}
public testMethod(object input)
{
int count = 1000;
for (int i = 0; i < count; i++)
{
Thread.Sleep(10);
double newvalue = ((double)i + 1) / (double)count;
Interlocked.Exchange(ref percentComplete, newvalue);
}
}
}
Then in your MainWindow class use a DispatcherTimer to periodically poll the progress information. Configure the DispatcherTimer to raise the Tick event on whatever interval is most appropriate for your situation.
public partial class MainWindow : Window
{
public void YourDispatcherTimer_Tick(object sender, EventArgs args)
{
YourProgressBar.Value = calculation.PercentComplete;
}
}
You're right that you should use the Dispatcher to update controls on the UI thread, and also right that long-running processes should not run on the UI thread. Even if you run the long-running process asynchronously on the UI thread, it can still cause performance issues.
It should be noted that Dispatcher.CurrentDispatcher will return the dispatcher for the current thread, not necessarily the UI thread. I think you can use Application.Current.Dispatcher to get a reference to the UI thread's dispatcher if that's available to you, but if not you'll have to pass the UI dispatcher in to your background thread.
Typically I use the Task Parallel Library for threading operations instead of a BackgroundWorker. I just find it easier to use.
For example,
Task.Factory.StartNew(() =>
SomeObject.RunLongProcess(someDataObject));
where
void RunLongProcess(SomeViewModel someDataObject)
{
for (int i = 0; i <= 1000; i++)
{
Thread.Sleep(10);
// Update every 10 executions
if (i % 10 == 0)
{
// Send message to UI thread
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action)(() => someDataObject.ProgressValue = (i / 1000)));
}
}
}
Everything that interacts with the UI must be called in the UI thread (unless it is a frozen object). To do that, you can use the dispatcher.
var disp = /* Get the UI dispatcher, each WPF object has a dispatcher which you can query*/
disp.BeginInvoke(DispatcherPriority.Normal,
(Action)(() => /*Do your UI Stuff here*/));
I use BeginInvoke here, usually a backgroundworker doesn't need to wait that the UI updates. If you want to wait, you can use Invoke. But you should be careful not to call BeginInvoke to fast to often, this can get really nasty.
By the way, The BackgroundWorker class helps with this kind of taks. It allows Reporting changes, like a percentage and dispatches this automatically from the Background thread into the ui thread. For the most thread <> update ui tasks the BackgroundWorker is a great tool.
If this is a long calculation then I would go background worker. It has progress support. It also has support for cancel.
http://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
Here I have a TextBox bound to contents.
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.Write("backgroundWorker_RunWorkerCompleted");
if (e.Cancelled)
{
contents = "Cancelled get contents.";
NotifyPropertyChanged("Contents");
}
else if (e.Error != null)
{
contents = "An Error Occured in get contents";
NotifyPropertyChanged("Contents");
}
else
{
contents = (string)e.Result;
if (contentTabSelectd) NotifyPropertyChanged("Contents");
}
}
You are going to have to come back to your main thread (also called UI thread) in order to update the UI.
Any other thread trying to update your UI will just cause exceptions to be thrown all over the place.
So because you are in WPF, you can use the Dispatcher and more specifically a beginInvoke on this dispatcher. This will allow you to execute what needs done (typically Update the UI) in the UI thread.
You migh also want to "register" the UI in your business, by maintaining a reference to a control/form, so you can use its dispatcher.
Thank God, Microsoft got that figured out in WPF :)
Every Control, like a progress bar, button, form, etc. has a Dispatcher on it. You can give the Dispatcher an Action that needs to be performed, and it will automatically call it on the correct thread (an Action is like a function delegate).
You can find an example here.
Of course, you'll have to have the control accessible from other classes, e.g. by making it public and handing a reference to the Window to your other class, or maybe by passing a reference only to the progress bar.
Felt the need to add this better answer, as nothing except BackgroundWorker seemed to help me, and the answer dealing with that thus far was woefully incomplete. This is how you would update a XAML page called MainWindow that has an Image tag like this:
<Image Name="imgNtwkInd" Source="Images/network_on.jpg" Width="50" />
with a BackgroundWorker process to show if you are connected to the network or not:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
public partial class MainWindow : Window
{
private BackgroundWorker bw = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
// Set up background worker to allow progress reporting and cancellation
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
// This is your main work process that records progress
bw.DoWork += new DoWorkEventHandler(SomeClass.DoWork);
// This will update your page based on that progress
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
// This starts your background worker and "DoWork()"
bw.RunWorkerAsync();
// When this page closes, this will run and cancel your background worker
this.Closing += new CancelEventHandler(Page_Unload);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BitmapImage bImg = new BitmapImage();
bool connected = false;
string response = e.ProgressPercentage.ToString(); // will either be 1 or 0 for true/false -- this is the result recorded in DoWork()
if (response == "1")
connected = true;
// Do something with the result we got
if (!connected)
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_off.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
else
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_on.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
}
private void Page_Unload(object sender, CancelEventArgs e)
{
bw.CancelAsync(); // stops the background worker when unloading the page
}
}
public class SomeClass
{
public static bool connected = false;
public void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
int i = 0;
do
{
connected = CheckConn(); // do some task and get the result
if (bw.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
Thread.Sleep(1000);
// Record your result here
if (connected)
bw.ReportProgress(1);
else
bw.ReportProgress(0);
}
}
while (i == 0);
}
private static bool CheckConn()
{
bool conn = false;
Ping png = new Ping();
string host = "SomeComputerNameHere";
try
{
PingReply pngReply = png.Send(host);
if (pngReply.Status == IPStatus.Success)
conn = true;
}
catch (PingException ex)
{
// write exception to log
}
return conn;
}
}
For more information: https://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
I am working on a winform application, and my goal is to make a label on my form visible to the user, and three seconds later make the label invisible. The issue here is timing out three seconds. I honestly do not know if this was the correct solution to my problem, but I was able to make this work by creating a new thread, and having the new thread Sleep for three seconds (System.Threading.Thread.Sleep(3000)).
I can't use System.Threading.Thread.Sleep(3000) because this freezes my GUI for 3 seconds!
private void someVoid()
{
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
Thread sleepThreadStart = new Thread(new ThreadStart(newThread_restProgram));
sleepThreadStart.Start();
// Once three seconds has passed / thread has finished: lbl_authenticationProcess.Visible = false;
}
private void newThread_restProgram()
{
System.Threading.Thread.Sleep(3000);
}
So, back to my original question. How can I determine (from my main thread) when the new thread has completed, meaning three seconds has passed?
I am open to new ideas as well as I'm sure there are many.
Right now, you are blocking the entire UI thread in order to hide a label after 3 seconds. If that's what you want, then just user Thread.Sleep(3000) from within the form. If not, though, then you're best off using a Timer:
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 3000;
timer.Tick += (s, e) => { this.lbl_authenticationProcess.Visible = false; timer.Stop(); }
timer.Start();
After 3 seconds, the label will disappear. While you're waiting for that, though, a user can still interact with your application.
Note that you must use the Forms version of Timer, since its Tick event is raised on the UI thread, allowing direct access to the control. Other timers can work, but interaction with the control would have to be Invoke/BeginInvoked.
Did you try to use Timer
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 3000;
t.Start();
t.Tick += new EventHandler(t_Tick);
void t_Tick(object sender, EventArgs e)
{
label.Visible = false;
}
You really don't need to synchronize anything. You just need a new thread, with a reference to your label. Your code is actually pretty close:
private void someVoid()
{
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
lbl_authenticationProcess.Visible = true;
Thread sleepThreadStart = new Thread(new ThreadStart(newThread_restProgram));
sleepThreadStart.Start();
}
private void newThread_restProgram()
{
System.Threading.Thread.Sleep(3000);
if (lbl_authenticationProcess.InvokeRequired) {
lbl_authenticationProcess.Invoke(new SimpleCallBack(makeInvisible));
} else {
makeInvisible();
}
}
private void makeInvisible()
{
lbl_authenticationProcess.Visible = false;
}
So, when someVoid() is called, the message on the label is set, the label is made visible. Then a new thread is started with the newThread_restProgram() as the body. The new thread will sleep for 3 seconds (allowing other parts of the program to run), then the sleep ends and the label is made invisible. The new thread ends automatically because it's body method returns.
You can make a method like so:
public void SetLbl(string txt)
{
Invoke((Action)(lbl_authenticationProcess.Text = txt));
}
And you would be able to call it from the second thread, but it invokes on the main thread.
If you're using .NET 3.5 or older, it's kinda a pain:
private void YourMethod()
{
someLabel.BeginInvoke(() =>
{
someLabel.Text = "Something Else";
Thread thread = new Thread(() =>
{
Thread.Sleep(3000);
someLabel.BeginInvoke(() => { someLabel.Visible = false; });
});
thread.Start();
});
}
That should stop you from blocking the UI.
If you're using .NET 4+:
Task.Factory.StartNew(() =>
{
someLabel.BeginInvoke(() => { someLabel.Text = "Something" });
}).ContinueWith(() =>
{
Thread.Sleep(3000);
someLabel.BeginInvoke(() => { someLabel.Visible = false; });
});
If you are willing to download the Async CTP then you could use this really elegant solution which requires the new async and await keywords.1
private void async YourButton_Click(object sender, EventArgs args)
{
// Do authentication stuff here.
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
await Task.Delay(3000); // TaskEx.Delay in CTP
lbl_authenticationProcess.Visible = false;
}
1Note that the Async CTP uses TaskEx instead of Task.
You can use an AutoResetEvent for your thread synchronization. You set the event to signalled when your secondary thread has woken from it's sleep, so that it can notify your main thread.
That means though that your main thread waits for the other thread to complete.
On that note, you can use SecondThread.Join() to wait for it to complete in your main thread.
You do either of the above, but you don't need to do both.
As suggested in the comments, having a UI thread sleep is not generally a good idea, as it causes unresponsiveness for the user.
However if you do that, you might as well just sleep your main thread and get rid of the extraneous need of the second thread.
I'm not exactly sure this is the right way to do it, but to answer your question, you have to use the Join() function.
public void CallingThread()
{
Thread t = new Thread(myWorkerThread);
t.Join();
}
public void WorkerThread()
{
//Do some stuff
}
You can also add a timeout as parameter to the function, but you don't need that here.
I've been trying to get the logic right for my timer and backgroundworker thread. Granted I don't fully understand the whole system despite all my reading. the following are excerpts of code concerned:
My polling button :
private void pollStart_Click(object sender, EventArgs e)
{
tst_bgw = new BackgroundWorker();
//mandatory. Otherwise will throw an exception when calling ReportProgress method
tst_bgw.WorkerReportsProgress = true;
//mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation
tst_bgw.WorkerSupportsCancellation = true;
tst_bgw.DoWork += tst_bgw_DoWork;
tst_bgw.ProgressChanged += tst_bgw_ProgressChanged;
tst_bgw.RunWorkerCompleted += tst_bgw_RunWorkerCompleted;
tst_bgw.RunWorkerAsync();
}
which I think is right so far
my Background worker thread:
private void tst_bgw_DoWork(object source, DoWorkEventArgs e)
{
m_timer = new System.Timers.Timer();
m_timer.Interval = 1000;
m_timer.Enabled = true;
m_timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
if (tst_bgw.CancellationPending)
{
e.Cancel = true;
return;
}
}
and the elapsed tier event code:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (powerVal > 3250)
{
m_timer.Stop();
tst_bgw.CancelAsync();
}
else
{
string pow;
int progressVal = 100 - ((3250 - powerVal) / timerVal);
uiDelegateTest tstDel = new uiDelegateTest(recvMessage);// the recvMessage function takes a textbox as an argument and directs output from socket to it.
pow = construct_command("power", powerVal);
sData = Encoding.ASCII.GetBytes(pow);
if (active_connection)
try
{
m_sock.Send(sData);
Array.Clear(sData, 0, sData.Length);
tstDel(ref unit_Output);// Read somewhere that you can only modify UI elements in this method via delegate so I think this is OK.
m_sock.Send(time_out_command);
tstDel(ref unit_Output);
tst_bgw.ReportProgress(progressVal);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
tst_bgw.ReportProgress(powerVal, progressVal);
powerVal = powerVal + pwrIncVal;
}
I'd just like to know a few other things; am I using the right timer (not that I think it should matter greatly but it was suggested that this might be the best timer for what I want to do) and canI really modify UI elements in the DoWork method only through delegates and if yes are there sepcial considerations to doing so.
Sorry about the long posting and thank you for your time.
There is lots wrong with this code.
1) You aren't disposing of your background worker. BackgroundWorkers must be disposed of after use. They are designed to be used as winforms components and would normally be added to a window via the designer. This will ensure it is created with the form and disposed of when the form is.
2) All you are doing in your dowork method is creating a new timer and running it. There is no point of doing this in a background worker because it will happen so quickly anyway.
3) You will recreate the timer every time you run the background worker again. But you aren't ever stopping or disposing of the old timer, you are just overwriting the member.
I recommend you get rid of the BackgroundWorker completely and just use a timer. Create the timer in the forms constructor and make sure you dispose of it in the forms dispose method. (Or use the designer to add it to the form). In the pollstart_click method just start the timer. (If you have a poll stop method, you can stop the timer in that)
You don't need both a BackgroundWorker and a Timer to accomplish your goal. From what you have posted it looks like you want to have the user click a button which starts a polling process that quits at a certian point.
Your polling model really suggests a timer would work just fine.
If you use a Timer I would Initialize the timer after the InitializeComponent() call with something like
private void InitializeTimer()
{
this.timer = new Timer();
int seconds = 1;
this.timer.Interval = 1000 * seconds; // 1000 * n where n == seconds
this.timer.Tick += new EventHandler(timer_Tick);
// don't start timer until user clicks Start
}
The button_click will simply
private void button_Click(object sender, EventArgs e)
{
this.timer.Start();
}
Then on the timer_Tick you will need to do your polling and you should be able to update your UI from there if the timer is on the UI thread like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set progressbar, label, etc with value from method above
}
}
However if the timer thread is not on the same thread as the UI you well get an exception while trying to update the UI. In that case you can use the Invoke that DataDink mentions and do something like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set a label with Invoke
mylabel.Invoke(
new MethodInvoker( delegate { mylabel.Text = "some string"; } )
);
}
}
Given the code you posted you really didn't need to do both a BackgroundWorker and a Timer, but I have had instances where I have used a BackgroundWorker to do work when a timer is called so that I could have a timer update UI periodically and have a manual button to Refresh the UI. But I wasn't updating my UI quite the way you are.
If you still have the need to do both, here is, roughly, how you can flow your app...
Create an
InitailizeBackgroundWorker() method
along with the InitializeTimer so you have
it already initalized before the
Timer fires.
Then set the Timer.Tick
to call the
BackgroundWorker.RunWorkerAsync()
Then you can do all the UI updates from within the RunWorkerAsync by
using the
BackgroundWorker.ReportProgress().