As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
Deleted post and have code:
namespace StackOverflowQuestion
{
class Program
{
static void Main (string[] args)
{
var cw = new ConsoleWriter ();
for (int i = 0; i <= 5000000; i++)
{
cw.Write (i);
}
Console.ReadKey ();
}
}
class ConsoleWriter
{
private Stopwatch sw = new Stopwatch ();
public ConsoleWriter ()
{
sw.Start ();
}
public void Write (int pNumber)
{
if (sw.ElapsedMilliseconds >= 50)
{
Console.WriteLine (pNumber);
sw.Restart ();
}
}
}
}
And the output:
305940
651171
1002965
1358665
1715740
2069602
2419054
2772833
3127880
3485054
3844335
4204016
4557912
4913494
So everything works fine. In this example ConsoleWriter display number on console, but it could be displayed in Control surface. Like you see, even if I call 5000000 times Write method, it only updates UI after minimum 50 ms. Great, but notice in many cases the last value 5000000 will not be displayed. How to fix it? Should I use a class (thread) which will call event each 50 ms and it will check the value to write is changed?
You could use a timer
Timer _timer;
public void StartTimer()
{
_timer = new Timer();
_timer.Interval = 100; // 100 ms = 0.1 s
_timer.Tick += new EventHandler(timer_Tick);
_timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
myControl.Number = i;
}
In the control there should be something like
private int _number;
public int Number
{
get { return _number; }
set
{
if (value != _number) {
_number = value;
Invalidate();
}
}
}
The call of Invalidate() will trigger the Paint event. Your painting logic should be in the OnPaint method:
protected override void OnPaint(PaintEventArgs e)
{
... paint here
}
But of cause the for loop itself will freeze the application. You could use a second timer that updates the counter at a faster intervall than your display counter. Every code running on the UI-tread (main thread if you prefer) will freeze you UI until it terminates. An easy way of doing a heavy work in the background in a separate thread is to use the BackgroundWorker. The background worker automatically switches between UI-thread and worker-thread and allows you to report progess to the UI-thread.
You can also start a thread manually in order to update the counter. If this will be the only thread changing the number, no synchronisation mechanisms will be required. But never access the UI (a form or a control) from another thread than the UI-thread.
Here is a complete non-blocking solution using another thread for the counting
Timer _timer;
int _counter;
System.Threading.Thread _worker;
public frmTimerCounter()
{
InitializeComponent();
_worker = new System.Threading.Thread(() =>
{
while (_counter < 10000000) {
_counter++;
System.Threading.Thread.Sleep(20);
}
});
_worker.Start();
StartTimer();
}
public void StartTimer()
{
_timer = new System.Windows.Forms.Timer();
_timer.Interval = 100; // 100 ms = 0.1 s
_timer.Tick += new EventHandler(timer_Tick);
_timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
// I used a Label for the test. Replace it by your control.
label1.Text = _counter.ToString();
}
You didn't post any code but I can guess at what it looks like. You are doing way too much work in the property setter. This for() loop should never take more than a millisecond, way too short to ever notice a GUI freeze.
You get this by following the standard way controls repaint themselves. Which is lazily. You get that by calling the Invalidate() method. Like this:
class MyControl : Control {
private int number;
public int Number {
get { return this.number; }
set {
if (value != this.number) this.Invalidate();
this.number = value;
}
}
protected override void OnPaint(PaintEventArgs e) {
// TODO: paint number
//...
base.OnPaint(e);
}
}
You'll now also discover something else, there's no point to using that for() loop anymore. There never was one in the first place, a human being cannot possibly see the incredible rate at which a modern processor can increment a number. So you'll now replace that with:
myControl.Number = 50000;
If you actually meant for a human eye to see the number increasing then you are going to have to do it a lot slower. No more than about once every 50 millisecond, about the point where the changes turn into a blur. That requires a Timer.
Related
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 2 years ago.
So I am trying to set my countdown timer intervals, so that 1hr = 10minutes (in real time), 1min = 10secs (In real time) and 1s = 0.17s (In realtime) to help when testing my code. I can't seem to find what part of my code to change without causing an error. so I tried defining the interval in the initialise components section and received this:
System.NullReferenceException: 'Object reference not set to an instance of an object.' timer was null.
private void button1_Click(object sender, EventArgs e)
{
AddTimeToClock(TimeSpan.FromSeconds(10));
}
private void Form1_Load(object sender, EventArgs e)
{
timer = new System.Windows.Forms.Timer();
timer.Interval = (int)TimeSpan.FromSeconds(1).TotalMilliseconds;
timer.Tick += OnTimeEvent;
DisplayTime();
}
private void DisplayTime()
{
lblTime.Text = countdownClock.ToString(#"hh\:mm\:ss");
}
private void OnTimeEvent(object sender, EventArgs e)
{
// Subtract whatever our interval is from the countdownClock
countdownClock = countdownClock.Subtract(TimeSpan.FromMilliseconds(timer.Interval));
if (countdownClock.TotalMilliseconds <= 0)
{
// Countdown clock has run out, so set it to zero
// (in case it's negative), and stop our timer
countdownClock = TimeSpan.Zero;
timer.Stop();
}
// Display the current time
DisplayTime();
}
private void AddTimeToClock(TimeSpan timeToAdd)
{
// Add time to our clock
countdownClock += timeToAdd;
// Display the new time
DisplayTime();
// Start the timer if it's stopped
//if (!timer.Enabled) timer.Start();
}
private void button2_Click(object sender, EventArgs e)
{
AddTimeToClock(TimeSpan.FromMinutes(1));
}
private void button3_Click(object sender, EventArgs e)
{
AddTimeToClock(TimeSpan.FromMinutes(10));
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked) timer.Start();
else
{
timer.Stop();
}
}
I would suggest you introduce a wrapper for your timer. That way you can expose a interval-method that is scaled however you want. For example:
public class ScaledTimer : IDisposable
{
private Timer timer;
public ScaledTimer() => this.timer = new Timer();
public void Dispose() => timer.Dispose();
public void Start() => timer.Start();
public void Stop() => timer.Stop();
public double Scale { get; set; } = 1;
public bool Enabled
{
get => timer.Enabled;
set => timer.Enabled = value;
}
public int Interval
{
get => (int)(timer.Interval * Scale);
set => timer.Interval = (int)(value / Scale);
}
public event EventHandler Tick
{
add => timer.Tick += value;
remove => timer.Tick -= value;
}
}
Set Scale to 10 to make the timer tick 10 times as fast. Note that this will not work well if you also use DateTime.Now since that will use the actual system time. It might also be a good idea to extract an interface for the timer. This can be useful when unit testing since it allows mocking of the timer. There is also a limited resolution for timers, so it may not work well if the interval is down to ~16ms.
If I'm understanding what you are trying to accomplish (ticking at the same speed, but logically running out your countdownClock at 6x speed), then I believe your best plan of action is to add a constant for time scale.
private static readonly int timeScale = 6
Then when you adjust your countdownClock in the OnTimeEvent, you can multiply by the scale.
countdownClock = countdownClock.Subtract(timeScale * TimeSpan.FromMilliseconds(timer.Interval));
Set your timeScale to 1 to run at normal speed, or to higher values to run faster.
This would give a general idea of how to accomplish your goal, but require hand-editing of the timeScale value when debugging to get your desired behavior. From here, you can decide how to best manage this - maybe you want to load your timeScale from a config file, or adjust it based on whether #DEBUG is defined. You may also want to make a countdownClock class that has a Scale property to further encapsulate this behavior. Final implementation is up to you.
I have a little problem. There is something like chess timer. When i press button, current timer stops and second starts, but after 1 second. How can i start second one immediately?
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1 {
public partial class Form1 : Form {
byte sec1;
byte sec2;
public Form1() {
InitializeComponent();
sec1 = 0;
sec2 = 0;
}
private void button1_Click(object sender , EventArgs e) {
timer1.Start();
timer2.Stop();
}
private void button2_Click(object sender , EventArgs e) {
timer2.Start();
timer1.Stop();
}
private void timer1_Tick(object sender , EventArgs e) {
label1.Text = sec1.ToString();
sec1++;
}
private void timer2_Tick(object sender , EventArgs e) {
label2.Text = sec2.ToString();
sec2++;
}
}
}
Edit
I know your question is "how to start the timers immediately", but in your code they are starting immediately. When you call start the timer starts. I believe the effect you are seeing is related to the delay associated with the tick event, which from the description I am assuming is set to a 1 second interval. Since you have said that you are trying to simulate something similar to a chess timer (although in your case counting up as opposed to down), then using something like a stop watch which can start, stop and show elapsed time would be a closer model. Since there is a Stopwatch class that provides exactly this behavior, I think it would be easier to implement it using two of those and just have a single background thread that updates the UI as frequently as needed. You could even add an update call into each button push to ensure the text boxes are up to date.
===============================
Maybe instead of the timers you should use two instances of the Stopwatch class. This will remove the need for your two variables that you are using to keep track of the seconds as the Stopwatch class will be holding the elapsed time for each counter.
Then in your button methods you could just do this:
private Stopwatch sw1 = new Stopwatch();
private Stopwatch sw2 = new Stopwatch();
private void button1_Click(object sender , EventArgs e) {
sw1.Start();
sw2.Stop();
}
private void button2_Click(object sender , EventArgs e) {
sw2.Start();
sw1.Stop();
}
And then you can use a Background worker or some other background thread that runs and updates your text boxes with the elapsed time from the timers you just need to grab the elapsed time.
// This will give you the total number of seconds elapsed.
var timer1Seconds = Math.Floor(sw1.Elapsed.TotalSeconds);
Here is an example of how you can make this update the UI:
private bool _stop = false;
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
while(!_stop)
{
UpdateElapsedTimes();
Thread.Sleep(1000);
}
}
}
private void UpdateElapsedTimes()
{
if (InvokeRequired)
{
Invoke(UpdateElapsedTimes());
return;
}
label1.Text = Math.Floor(sw1.Elapsed.TotalSeconds).ToString();
label2.Text = Math.Floor(sw2.Elapsed.TotalSeconds).ToString();
}
Note - in a production program I would not use a boolean as my loop checker, you would use an event handle, and probably a couple of event handles if you wanted to allow pausing the updates, this is just to show an idea of how to do it. You could invoke directly from the thread method and drop the InvokeRequired check, but I added that for additional safety and since it was there I skipped it in the loop.
The timer does start immediately. The problem is that you are not reporting fractions of seconds, so the display will show 0 until a full second has elapsed, which is accurate, technically.
If you want to show 1 immediately, just initialize your variables that way.
public Form1() {
InitializeComponent();
sec1 = 1;
sec2 = 1;
}
Here's my situation:
I have a WPF application, where I have a method which takes a lot of time to be completed. I don't want to lose UI responsiveness, so I'd like to call that method in another thread.
I won't paste here my entire code, because it's too long, instead I wrote this short program, which represents well what I'm dealing with:
public void MainWindow()
{
InitializeComponent();
ProcessThread = new Thread(TimeConsumingMethod);
ProcessThread.Name = "ProcessThread";
ProcessThread.Start();
}
public void TimeConsumingMethod()
{
this.Dispatcher.Invoke(() =>
{
MytextBlock.Text = "new text";
MyOtherTextBlock.Text = "Hello";
});
for (int i = 0; i < 50; i++)
{
Debug.WriteLine("Debug line " + i);
}
if (MyRadioButton.IsChecked == false) //????????????????
{
while (true)
{
if (DateTime.Now >= timePicker.Value)
break;
}
}
OtherMethod();
}
Actually, I have two questions for the above code:
1. Everytime I want to access UI controls in my code I have to use this.Dispatcher.Invoke() =>.... Is it the right thing to do? I mean, I have a few places in my method (in my real code) where I check the state of some controls and everytime I need to do his Dispatcher.invoke thing - isn't there a better way to acces these controls?
2. In the code above, there's IF block in the end - in that block I'm checking the state of my RadioButton. Inside of that IF, I have a time consuming code. I cannot just do this:
this.Dispatcher.Invoke(() =>
{
if (MyRadioButton.IsChecked == false) //????????????????
{
while (true)
{
if (DateTime.Now >= timePicker.Value)
break;
}
}
});
That code would tell my UI thread to handle this if block - but I don't want that! That would cause the whole UI to freeze until this IF block gets done. How should I handle this situation?
Well, there are a lot of ways to implement what you are trying to do. One of them might look like this:
public MainWindow() {
InitializeComponent();
Initialize(); //do some intialization
}
private async void Timer_Tick(object sender, EventArgs e) {
if (DateTime.Now >= timePicker.SelectedDate) { //check your condition
timer.Stop(); //probably you need to run it just once
await Task.Run(() => OtherMethod()); //instead of creating thread manually use Thread from ThreadPool
//use async method to avoid blocking UI during long method is running
}
}
private readonly DispatcherTimer timer = new DispatcherTimer(); //create a dispatcher timer that will execute code on UI thread
public void Initialize() {
MytextBlock.Text = "new text";
MyOtherTextBlock.Text = "Hello"; //access UI elements normally
for (var i = 0; i < 50; i++) {
Debug.WriteLine("Debug line " + i);
}
if (MyRadioButton.IsChecked == false)
{
timer.Interval = TimeSpan.FromSeconds(10); // during init setup timer instead of while loop
timer.IsEnabled = true;
timer.Tick += Timer_Tick; //when 10 sec pass, this method is called
timer.Start();
}
}
public void OtherMethod() {
//long running method
Thread.Sleep(1000);
}
I've added some comments, but the main idea is this:
Don't create threads manually, use ThreadPool
Don't loop to wait for something, use timer to periodically check for it
Use async method when you have I/O Tasks
I have a C# application (winforms and wpf, but for this question I'm focusing on the winforms one) where a backgroundWorker is used to work on a data set, and the call to ProgressChanged which then calls the form Refresh method to force a repaint. This then paints a bunch of ellipses based on the current frame of the data set.
A given frame may involve drawing anywhere between zero and several hundred ellipses.
In addition, I have a slider control that allows the user to adjust the playback rate (basically the thread.sleep value within the loop.)
When the user sets the sleep value too low, sometimes the repainting methods get queued up, and the UI becomes unresponsive. (This depends on the number of ellipses in the frame, and the speed of the computer. And the delay is 100% with the repainting on the UI, not with any other processing, which is basically just incrementing a counter and setting a label text.)
I would like to be able to detect the queuing up and automatically adjust the speed slider to accommodate a larger data set and/or slower computer. How can I tell if the UI thread is backed up with multiple calls to Map_Paint?
Current code (paraphrased):
public Map()
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
_worker.ProgressChanged += _worker_ProgressChanged;
_worker.WorkerReportsProgress = true;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
_frameCount = _frames.FrameCount();
// For this specific example, _frameCount may be around 30000-40000
for (var i = 0; i < _frameCount; i++)
{
var f = _frames.Frame(i + 1);
_worker.ReportProgress(i, f);
Thread.Sleep(_tickCount);
_suspend.WaitOne(); // Used to Pause the playback
}
}
void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// set some variables according to state and progresspercentage snipped here
// ...
// I would like to detect at this point whether the screen repainting is backed up
// and if so, adjust the value of _tickCount to slow down the program.
this.Refresh();
}
private void Map_Paint(object sender, PaintEventArgs e)
{
// Lots of ellipsis drawing stuff here
// Maybe 0-1000 ellipses drawn per cycle.
}
private void tbSpeed_Scroll(object sender, EventArgs e)
{
// This is the Scroll event for the slider.
// Value range is 10-300
// The slider becomes unresponsive when the UI thread backs up.
// I'd like to detect the back up and override the value of _tickCount
_tickCount = tbSpeed.Value;
}
private static object _lock = new object();
private static int _queuedCount = 0;
public Map()
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
_worker.ProgressChanged += _worker_ProgressChanged;
_worker.WorkerReportsProgress = true;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
_frameCount = _frames.FrameCount();
// For this specific example, _frameCount may be around 30000-40000
for (var i = 0; i < _frameCount; i++)
{
var f = _frames.Frame(i + 1);
lock(_lock)
{
_queuedCount++;
}
_worker.ReportProgress(i, f);
Thread.Sleep(_tickCount);
_suspend.WaitOne(); // Used to Pause the playback
}
}
void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (_queuedCount > 1)
//now queue is building up
this.Refresh();
lock(_lock)
{
_queuedCount--;
}
}
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.