How to invoke a UI method from another thread - c#

Playing round with Timers.
Context: a winforms with two labels.
I would like to see how System.Timers.Timer works so I've not used the Forms timer.
I understand that the form and myTimer will now be running in different threads.
Is there an easy way to represent the elapsed time on lblValue in the following form?
I've looked here on MSDN but is there an easier way !
Here's the winforms code:
using System.Timers;
namespace Ariport_Parking
{
public partial class AirportParking : Form
{
//instance variables of the form
System.Timers.Timer myTimer;
int ElapsedCounter = 0;
int MaxTime = 5000;
int elapsedTime = 0;
static int tickLength = 100;
public AirportParking()
{
InitializeComponent();
keepingTime();
lblValue.Text = "hello";
}
//method for keeping time
public void keepingTime() {
myTimer = new System.Timers.Timer(tickLength);
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
void myTimer_Elapsed(Object myObject,EventArgs myEventArgs){
myTimer.Stop();
ElapsedCounter += 1;
elapsedTime += tickLength;
if (elapsedTime < MaxTime)
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (ElapsedCounter % 2 == 0)
this.lblValue.Text = "hello world";
else
this.lblValue.Text = "hello";
myTimer.Start();
}
else
{ myTimer.Start(); }
}
}
}

I guess your code is just a test so I won't discuss about what you do with your timer. The problem here is how to do something with an user interface control inside your timer callback.
Most of Control's methods and properties can be accessed only from the UI thread (in reality they can be accessed only from the thread where you created them but this is another story). This is because each thread has to have its own message loop (GetMessage() filters out messages by thread) then to do something with a Control you have to dispatch a message from your thread to the main thread. In .NET it is easy because every Control inherits a couple of methods for this purpose: Invoke/BeginInvoke/EndInvoke. To know if executing thread must call those methods you have the property InvokeRequired. Just change your code with this to make it works:
if (elapsedTime < MaxTime)
{
this.BeginInvoke(new MethodInvoker(delegate
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (ElapsedCounter % 2 == 0)
this.lblValue.Text = "hello world";
else
this.lblValue.Text = "hello";
}));
}
Please check MSDN for the list of methods you can call from any thread, just as reference you can always call Invalidate, BeginInvoke, EndInvoke, Invoke methods and to read InvokeRequired property. In general this is a common usage pattern (assuming this is an object derived from Control):
void DoStuff() {
// Has been called from a "wrong" thread?
if (InvokeRequired) {
// Dispatch to correct thread, use BeginInvoke if you don't need
// caller thread until operation completes
Invoke(new MethodInvoker(DoStuff));
} else {
// Do things
}
}
Note that current thread will block until UI thread completed method execution. This may be an issue if thread's timing is important (do not forget that UI thread may be busy or hung for a little). If you don't need method's return value you may simply replace Invoke with BeginInvoke, for WinForms you don't even need subsequent call to EndInvoke:
void DoStuff() {
if (InvokeRequired) {
BeginInvoke(new MethodInvoker(DoStuff));
} else {
// Do things
}
}
If you need return value then you have to deal with usual IAsyncResult interface.
How it works?
A GUI Windows application is based on the window procedure with its message loops. If you write an application in plain C you have something like this:
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
With these few lines of code your application wait for a message and then delivers the message to the window procedure. The window procedure is a big switch/case statement where you check the messages (WM_) you know and you process them somehow (you paint the window for WM_PAINT, you quit your application for WM_QUIT and so on).
Now imagine you have a working thread, how can you call your main thread? Simplest way is using this underlying structure to do the trick. I oversimplify the task but these are the steps:
Create a (thread-safe) queue of functions to invoke (some examples here on SO).
Post a custom message to the window procedure. If you make this queue a priority queue then you can even decide priority for these calls (for example a progress notification from a working thread may have a lower priority than an alarm notification).
In the window procedure (inside your switch/case statement) you understand that message then you can peek the function to call from the queue and to invoke it.
Both WPF and WinForms use this method to deliver (dispatch) a message from a thread to the UI thread. Take a look to this article on MSDN for more details about multiple threads and user interface, WinForms hides a lot of these details and you do not have to take care of them but you may take a look to understand how it works under the hood.

Personally when I work in an application that works with threads out of the UI one, I usually write this little snippet:
private void InvokeUI(Action a)
{
this.BeginInvoke(new MethodInvoker(a));
}
When I do an async call in a different thread I can always callback using:
InvokeUI(() => {
Label1.Text = "Super Cool";
});
Simple and clean.

As asked, here is my answer that checks for cross thread calls, synchronises variable updates, doesen't stop and start the timer and doesn't use the timer for counting elapsed time.
EDIT fixed BeginInvoke call. I've done the cross thread invoke using a generic Action, This allows the sender and eventargs to be passed. If these are unused (as they are here) it is more efficient to use MethodInvoker but I suspect the handling would need to be moved into a parameterless method.
public partial class AirportParking : Form
{
private Timer myTimer = new Timer(100);
private int elapsedCounter = 0;
private readonly DateTime startTime = DateTime.Now;
private const string EvenText = "hello";
private const string OddText = "hello world";
public AirportParking()
{
lblValue.Text = EvenText;
myTimer.Elapsed += MyTimerElapsed;
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
private void MyTimerElapsed(object sender,EventArgs myEventArgs)
{
If (lblValue.InvokeRequired)
{
var self = new Action<object, EventArgs>(MyTimerElapsed);
this.BeginInvoke(self, new [] {sender, myEventArgs});
return;
}
lock (this)
{
lblElapsedTime.Text = DateTime.Now.SubTract(startTime).ToString();
elapesedCounter++;
if(elapsedCounter % 2 == 0)
{
lblValue.Text = EvenText;
}
else
{
lblValue.Text = OddText;
}
}
}
}

First, in Windows Forms (and most frameworks), a control can only be accessed (unless documented as "thread safe") by the UI thread.
So this.lblElapsedTime.Text = ... in your callback is plain wrong. Take a look at Control.BeginInvoke.
Second, You should use System.DateTime and System.TimeSpan for your time computations.
Untested:
DateTime startTime = DateTime.Now;
void myTimer_Elapsed(...) {
TimeSpan elapsed = DateTime.Now - startTime;
this.lblElapsedTime.BeginInvoke(delegate() {
this.lblElapsedTime.Text = elapsed.ToString();
});
}

Ended up using the following. It's a combination of the suggestions given:
using System.Timers;
namespace Ariport_Parking
{
public partial class AirportParking : Form
{
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//instance variables of the form
System.Timers.Timer myTimer;
private const string EvenText = "hello";
private const string OddText = "hello world";
static int tickLength = 100;
static int elapsedCounter;
private int MaxTime = 5000;
private TimeSpan elapsedTime;
private readonly DateTime startTime = DateTime.Now;
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public AirportParking()
{
InitializeComponent();
lblValue.Text = EvenText;
keepingTime();
}
//method for keeping time
public void keepingTime() {
using (System.Timers.Timer myTimer = new System.Timers.Timer(tickLength))
{
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
}
private void myTimer_Elapsed(Object myObject,EventArgs myEventArgs){
elapsedCounter++;
elapsedTime = DateTime.Now.Subtract(startTime);
if (elapsedTime.TotalMilliseconds < MaxTime)
{
this.BeginInvoke(new MethodInvoker(delegate
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (elapsedCounter % 2 == 0)
this.lblValue.Text = EvenText;
else
this.lblValue.Text = OddText;
}));
}
else {myTimer.Stop();}
}
}
}

Related

Deadlock when updating a UI control from a worker thread

To simplify the explanation of the strange behavior I am experiencing, I have this simple class named Log which fires 1 log events every 1000msec.
public static class Log
{
public delegate void LogDel(string msg);
public static event LogDel logEvent;
public static void StartMessageGeneration ()
{
for (int i = 0; i < 1000; i++)
{
logEvent.Invoke(i.ToString());
Task.Delay(1000);
}
}
}
I have the Form class below which is subscribed to the log events of the Log class so it can handle them and display in a simple text box.
Once a log message arrives, it is added to a list. Every 500msec, a timer object access that list so its content can be displayed in a text box.
public partial class Form1 : Form
{
private SynchronizationContext context;
private System.Threading.Timer guiTimer = null;
private readonly object syncLock = new object();
private List<string> listOfMessages = new List<string>();
public Form1()
{
InitializeComponent();
context = SynchronizationContext.Current;
guiTimer = new System.Threading.Timer(TimerProcessor, this, 0, 500);
Log.logEvent += Log_logEvent;
}
private void Log_logEvent(string msg)
{
lock (syncLock)
listOfMessages.Add(msg);
}
private void TimerProcessor(object obj)
{
Form1 myForm = obj as Form1;
lock (myForm.syncLock)
{
if (myForm.listOfMessages.Count == 0)
return;
myForm.context.Send(new SendOrPostCallback(delegate
{
foreach (string item in myForm.listOfMessages)
myForm.textBox1.AppendText(item + "\n");
}), null);
listOfMessages.Clear();
}
}
private void button1_Click(object sender, EventArgs e)
{
Log.StartMessageGeneration();
}
}
The problem I see is that sometimes, there is a dead lock (application stuck). Seems that the 2 locks (1st one for adding to the list and the 2nd one for "retrieving" from the list) are somehow blocking each others.
Hints:
1) reducing the rate of sending the messages from 1 sec to 200msec seems to help (not sure why)
2) Somehow something happens when returning to the GUI thread (using the synchronization context) and accessing the GUI control. If I don't return to the GUI thread, the 2 locks are working fine together...
Thanks everyone!
There's a few problems with your code, and a few... silly things.
First, your Log.StartMessageGeneration doesn't actually produce a log message every second, because you're not awaiting the task returned by Task.Delay - you're basically just creating a thousand timers very quickly (and pointlessly). The log generation is limited only by the Invoke. Using Thread.Sleep is a blocking alternative to Task.Delay if you don't want to use Tasks, await etc. Of course, therein lies your biggest problem - StartMessageGeneration is not asynchronous with respect to the UI thread!
Second, there's little point in using System.Threading.Timer on your form. Instead, just use the windows forms timer - it's entirely on the UI thread so there's no need for marshalling your code back to the UI thread. Since your TimerProcessor doesn't do any CPU work and it only blocks for a very short time, it's the more straight-forward solution.
If you decide to keep using System.Threading.Timer anyway, there's no point in manually dealing with synchronization contexts - just use BeginInvoke on the form; the same way, there's no point in passing the form as an argument to the method, since the method isn't static. this is your form. You can actually see this is the case since you omitted myForm in listOfMessages.Clear() - the two instances are the same, myForm is superfluous.
A simple pause in the debugger will easily tell you where the program is hung - learn to use the debugger well, and it will save you a lot of time. But let's just look at this logically. StartMessageGeneration runs on the UI thread, while System.Threading.Timer uses a thread-pool thread. When the timer locks syncLock, StartMessageGeneration can't enter the same lock, of course - that's fine. But then you Send to the UI thread, and... the UI thread can't do anything, since it's blocked by StartMessageGeneration, which never gives the UI an opportunity to do anything. And StartMessageGeneration can't proceed, because it's waiting on the lock. The only case where this "works" is when StartMessageGeneration runs fast enough to complete before your timer fires (thus freeing the UI thread to do its work) - which is very much possible due to your incorrect use of Task.Delay.
Now let's look on your "hints" with all we know. 1) is simply your bias in measurements. Since you never wait on the Task.Delay in any way, changing the interval does absolutely nothing (with a tiny change in case the delay is zero). 2) of course - that's where your deadlock is. Two pieces of code that depend on a shared resource, while they both require to take posession of another resource. It's a very typical case of a deadlock. Thread 1 is waiting for A to release B, and thread 2 is waiting for B to release A (in this case, A being syncLock and B being the UI thread). When you remove the Send (or replace it with Post), thread 1 no longer has to wait on B, and the deadlock disappears.
There's other things that make writing code like this simpler. There's little point in declaring your own delegate when you can just use Action<string>, for example; using await helps quite a bit when dealing with mixed UI/non-UI code, as well as managing any kind of asynchronous code. You don't need to use event where a simple function will suffice - you can just pass that delegate to a function that needs it if that makes sense, and it may make perfect sense not to allow multiple event handlers to be called. If you decide to keep with the event, at least make sure it conforms to the EventHandler delegate.
To show how your code can be rewritten to be a bit more up-to-date and actually work:
void Main()
{
Application.Run(new LogForm());
}
public static class Log
{
public static async Task GenerateMessagesAsync(Action<string> logEvent,
CancellationToken cancel)
{
for (int i = 0; i < 1000; i++)
{
cancel.ThrowIfCancellationRequested();
logEvent(i.ToString());
await Task.Delay(1000, cancel);
}
}
}
public partial class LogForm : Form
{
private readonly List<string> messages;
private readonly Button btnStart;
private readonly Button btnStop;
private readonly TextBox tbxLog;
private readonly System.Windows.Forms.Timer timer;
public LogForm()
{
messages = new List<string>();
btnStart = new Button { Text = "Start" };
btnStart.Click += btnStart_Click;
Controls.Add(btnStart);
btnStop =
new Button { Text = "Stop", Location = new Point(80, 0), Enabled = false };
Controls.Add(btnStop);
tbxLog = new TextBox { Height = 200, Multiline = true, Dock = DockStyle.Bottom };
Controls.Add(tbxLog);
timer = new System.Windows.Forms.Timer { Interval = 500 };
timer.Tick += TimerProcessor;
timer.Start();
}
private void TimerProcessor(object sender, EventArgs e)
{
foreach (var message in messages)
{
tbxLog.AppendText(message + Environment.NewLine);
}
messages.Clear();
}
private async void btnStart_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
var cts = new CancellationTokenSource();
EventHandler stopAction = (_, __) => cts.Cancel();
btnStop.Click += stopAction;
btnStop.Enabled = true;
try
{
await Log.GenerateMessagesAsync(message => messages.Add(message), cts.Token);
}
catch (TaskCanceledException)
{
messages.Add("Cancelled.");
}
finally
{
btnStart.Enabled = true;
btnStop.Click -= stopAction;
btnStop.Enabled = false;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
timer.Dispose();
btnStart.Dispose();
btnStop.Dispose();
tbxLog.Dispose();
}
base.Dispose(disposing);
}
}
SynchronizationContext.Send is run synchronously. When you call it, you actually block the UI thread until the operation is complete. But if UI thread is already in lock state, then it just make sense that you are in deadlock.
You can use SynchronizationContext.Post to avoid this.
I just answer on your question, but the truth is that your code need a "little" refactoring..

Continuously check if condition in different thread

I have two datetimes. One is current and another is the datetime when race starts.Now I want check this Minutes difference continuously in background thread(I dont know about thread). And when it meets the if(remainingMinutes <=4) I want to update the UI. How to implement this with thread in background?
public RelayCommand OpenSetBets
{
get { return _setBets ?? (_setBets = new RelayCommand(ExecuteSetBets, CanExecuteSetBets)); }
}
private void ExecuteSetBets()
{
_navigation.NavigationToSetBetsDialogue();
}
private bool CanExecuteSetBets()
{
// Thread t = new Thread(newthread);
double? remainingMinutes = null;
if (UK_RaceDetail.Count() != 0)
{
//t.Start();
DateTime CurrentUTCtime = DateTime.UtcNow;
DateTime NextRaceTime = UK_RaceDetail[0].One.Time;
remainingMinutes = NextRaceTime.Subtract(CurrentUTCtime).TotalMinutes;
}
if (remainingMinutes <= 4)
{
return true;
}
else
{
return false;
}
}
Updated Code.
I want to enable button if race is going to start in next 4 minutes.
If you only want to use your background task to monitor the date/time, I recommend you not to create a new Thread.
For WPF, instead of thread, you can try to use DispatcherTimer object with its Tick event
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
For WinForms, you can use Timer object with its Tick event
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Tick += timer_Tick;
This way, you are rather saved from more complex solution using Thread or ThreadPool
Edit: to use Task.Delay, as some prefer this rather "cleaner" way, see the comment by Mr. Aron
You can use Task.Run like,
System.Threading.Tasks.Task.Run(async () =>
{
//starts running in threadpool parallelly
while (!CanExecuteSetBets())
{
await Task.Delay(500); //wait 500 milliseconds after every check
}
DoYourWork(); //trigger your work here
}).ConfigureAwait(false);

Calling a method after set amount of time and/or aborting thread issues

So I've got an application that employs a filesystemWatcher and triggers an event just fine. The FSW will trigger a bunch of times pretty close together. I want to create a function that triggers say an hour after the last time the FSW was triggered.
I first tried using a backgroundworker: (All code is shortened for clarity)
namespace Devo
{
public partial class Form1 : Form
{
BackgroundWorker bw = new BackgroundWorker();
private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
if (bw.IsBusy)
{
bw.CancelAsync(); //this is to, in a way, reset the timer for the delayed method.
}
//do a lot of stuff
bw.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Stopwatch sw = new Stopwatch();
sw.Start();
while(sw.ElapsedMilliseconds < 180000)
{
if (bw.CancellationPending == true)
{
sw.Stop();
sw.Reset();
e.Cancel = true;
return;
}
}
sw.Stop();
sw.Reset();
DelayedMethod();
}
}
}
This didn't work as the second time bw.RunWorkerAsync() was called it was apparently busy, even though the call to bw.CancelAsync().
My next attempt involved a regular thread as I read somewhere on SO (can't find the link now) that one could not "restart" a backgroundWorker as I am trying to do.
The thread attemp is nearly identical but I thought I'd try in since there might be some constraints within the backgroundWorker that is not present in a regular thread. I thought.
namespace Devo
{
public partial class Form1 : Form
{
Thread PWC_counter_thread = new Thread(PWC_Counter);
private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
if (PWC_counter_thread.IsAlive)
PWC_counter_thread.Abort();
//do a lot of stuff
PWC_counter_thread.Start();
}
static void PWC_Counter()
{
Thread.Sleep(180000);
DelayedMethod();
}
}
}
But this gave me the same error. On the second call to PWC_counter_thread.Start() is was busy.
I'm assuming that a race condition is not present as the second thread waits for, in this example, 3 minutes, and the initial FSW method takes a good full second to execute, therefore I believe that the call to .Abort() and .CancelAsync() both are done before their respective methods are completed.
Now for the questions:
Is it possible to restart a thread in the fashion I am trying? If so, what am I doing wrong?
Should I delay my method call in another way? If so, tips?
EDIT/UPDATE/SOLUTION
I never got starting and stopping a thread to work as I wanted so I found another solution to my situation.
The situation was that I had a second thread that worked as a sort of timer where it would call a method after a set amount of time. My first thread did some work and upon finishing it would start the second thread. If the first thread got fired up again before the timer-thread had finished it was supposed to kill the thread and restart it.
This proved, for me, to be difficult to get the way I wanted. So I instead took another approach towards my wanted end result. Instead of restarting the thread I simply restarted the stopwatch that my second thread was using as a counter. This gave me the result I wanted. It's probably bad practice but it works.
In your BackgroundWorker example you probably have an issue with racing. CancelAsync(), as its name implies, is an asynchronious call, meaning that BackgroundWorker does not stop working immediately and it might still work when try to restart it. To avoid that, you should subscribe to RunWorkerCompleted event and wait for it to fire before calling bw.RunWorkerAsync(); again. For example:
public Form1()
{
bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnCompleted;
}
private BackgroundWorker bw;
private ManualResetEvent completed = new ManualResetEvent(false);
private void OnCompleted(object sender, RunWorkerCompletedEventArgs e)
{
completed.Set();
}
private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
if (bw.IsBusy)
{
bw.CancelAsync();
completed.WaitOne();
}
//do a lot of stuff
completed.Reset();
bw.RunWorkerAsync();
}
You have multiple issues with your Thread-based example.
You should never call Thread.Abort(). Instead, you should implement a cancellation mechanism, similar to that of BackgroundWorker. Make a bool field (_isCancelled or something) and check it periodically in thread delegate.
You can not reuse a Thread object. You should always create a new one.
You would be best off encapsulating this in a class, and use a System.Threading.Timer to detect the inactivity.
Here's an example I put together. The idea is that you create an InactivityDetector with the appropriate inactivity threshold (an hour in your case) and a callback method that will be called when that period of inactivity is exceeded.
You have to call InactivityDetector.RegisterActivity() whenever activity is detected (e.g. in your case a file creation is detected).
Once the inactivity callback has been issued, it will not be called again until RegisterActivity() has been called again (this prevents multiple callbacks for the same period of extended inactivity).
Your code would pass DelayedMethod for the inactivity Action delegate.
Note that the callback is on a separate thread!
(Also note that I didn't put in any parameter validation, to keep the code shorter.)
using System;
using System.Threading;
namespace ConsoleApp1
{
sealed class Program
{
void test()
{
using (var inactivityDetector = new InactivityDetector(TimeSpan.FromSeconds(2), inactivityDetected))
{
for (int loop = 0; loop < 3; ++loop)
{
Console.WriteLine("Keeping busy once a second for 5 seconds.");
for (int i = 0; i < 5; ++i)
{
Thread.Sleep(1000);
Console.WriteLine("Registering activity");
inactivityDetector.RegisterActivity();
}
Console.WriteLine("Entering 3 second inactivity");
Thread.Sleep(3000);
inactivityDetector.RegisterActivity();
}
}
}
static void inactivityDetected()
{
Console.WriteLine("Inactivity detected.");
}
static void Main(string[] args)
{
new Program().test();
}
}
public sealed class InactivityDetector: IDisposable
{
public InactivityDetector(TimeSpan inactivityThreshold, Action onInactivity)
{
_inactivityThreshold = inactivityThreshold;
_onInactivity = onInactivity;
_timer = new Timer(timerCallback, null, (int)inactivityThreshold.TotalMilliseconds, -1);
}
public void RegisterActivity()
{
_timer.Change(-1, -1);
_timer.Change((int)_inactivityThreshold.TotalMilliseconds, -1);
}
private void timerCallback(object state)
{
_timer.Change(-1, -1);
_onInactivity();
}
public void Dispose()
{
_timer.Dispose();
}
private readonly TimeSpan _inactivityThreshold;
private readonly Action _onInactivity;
private readonly Timer _timer;
}
}

My Custom progressBar Form Hangs when i show it

I made a form that plays a progressbar role here's the code i made
public partial class PXProgressBar : Form
{
public delegate bool CancelEvent();
public event CancelEvent cancel_e;
public Boolean ProcessCancelled
{
get;
set;
}
public PXProgressBar(bool EnableCancel)
{
InitializeComponent();
ProcessCancelled = false;
progressBar1.Minimum = 0;
if (!EnableCancel)
Cancelbtn.Visible = false;
}
public void increament(int step)
{
if (progressBar1.Value < progressBar1.Maximum-1)
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
progressBar1.Refresh();
}
else
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
if (this.TopMost)
this.TopMost = false;
this.Update();
this.Hide();
this.WindowState = FormWindowState.Minimized;
// this.Dispose();
}
}
public void SetMaximum(int MaximumValue)
{
if (MaximumValue <= 0)
{
progressBar1.Maximum = 0;
return;
}
if (progressBar1.Minimum != 0 && MaximumValue < progressBar1.Minimum)
{
progressBar1.Maximum = progressBar1.Minimum;
return;
}
progressBar1.Maximum = MaximumValue;
}
public void SetMinimum(int MinimumValue)
{
progressBar1.Value = 0;
if (MinimumValue <= 0)
{
progressBar1.Minimum = 0;
return;
}
if (progressBar1.Maximum != 100 && MinimumValue > progressBar1.Maximum)
{
progressBar1.Minimum = progressBar1.Maximum;
return;
}
progressBar1.Minimum= MinimumValue;
}
public void SetTitle(string ProcessTitle)
{
this.ProgressTitlelb.Text =ProcessTitle;// ProcessTitle;
//this.ProgressTitlelb.Left = (this.panel1.Width - this.ProgressTitlelb.Width) / 2;
//this.ProgressTitlelb.Top = (this.panel1.Height - this.ProgressTitlelb.Height) / 2;
this.Update();
}
private void Cancelbtn_Click(object sender, EventArgs e)
{
ProcessCancelled = true;
bool disposeRequired =cancel_e();
if(disposeRequired)
this.Dispose();
}
private void PXProgressBar_Shown(object sender, EventArgs e)
{
this.Update();
}
}
and i call the form through this code
if (ProgressBar == null)
ProgressBar = new PXProgressBar(true);
ProgressBar.SetTitle("Saving ...");
ProgressBar.SetMinimum(0);
ProgressBar.SetMaximum(100);
ProgressBar.TopMost = true;
ProgressBar.Show();
Application.DoEvents();
regarding that the past few lines are in a unction that is called throught a thread
but when i run it the form hangs so i cant set a Cancel Button in the form to let the user cancel the operation
Your code looks like it should be fine so I can only assume that you are doing a long running operation on the UI thread which would cause the UI to look like its hung. You need to perform long running operations on a background thread so that the UI thread remains responsive enough to respond to button clicks etc. There are many many articles about this if you consult your friend Google.
More info on that here http://www.idevforfun.com/index.php/2010/01/10/windows-ui-threading/
I agree with Skizz on the DoEvents call ... there's only a very few rare cases where that call is needed and mostly its in the framework itself that it gets used.
You need to make sure the GUI elements are created on the main form thread and not from a separate thread. So, you need to get you thread that is doing the work to get the main form thread to display and update the progress bar. This is going to take a bit of refactoring.
So, in your worker thread:
void DoWork () // of whatever it's called
{
main_form.CreateProgressBar ();
while (doing stuff)
{
main_form.IncrementProgressBar ();
do stuff
}
main_form.DestroyProgressBar ();
}
And in the main form:
delegate void Callback ();
void CreateProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (CreateProgressBar));
}
else
{
progress_bar = CreateProgressBar ();
}
}
void IncrementProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (IncrementProgressBar ));
}
else
{
progress_bar.IncrementProgressBar ();
}
}
void DestroyProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (DestroyProgressBar));
}
else
{
progress_bar.Close ();
progress_bar = null;
}
}
The InvokeRequired determines if the calling thread is the same as the GUI thread. If the calling thread is not the GUI thread, the Invoke is used to changed thread context. This is the synchronous version and won't complete until the invoked method is finished. There is an asynchronous version called BeginInvoke but this isn't really needed for what your doing.
The problem might be the DoEvents method call. From this MSDN page:
Calling this method causes the current thread to be suspended while
all waiting window messages are processed. If a message causes an
event to be triggered, then other areas of your application code may
execute. This can cause your application to exhibit unexpected
behaviors that are difficult to debug. If you perform operations or
computations that take a long time, it is often preferable to perform
those operations on a new thread. For more information about
asynchronous programming, see Asynchronous Programming Overview.
I don't think the DoEvents call is necessary. If you need to halt the code after the Show for the operation to complete, then use a System.Threading.EventWaitHandle instead.
There's some link maybe helpful for you about progressbar:
How do I implement a progress bar in C#?
Hope this help.
Just create other thread for progressbar and use it in background

How can I synchronize these two threads?

here is the class:
public class Ticker
{
public event EventHandler Tick;
public EventArgs e = null;
public void TickIt()
{
while (true)
{
System.Threading.Thread.Sleep(300);
if (Tick != null)
{
Tick(this, e);
}
}
}
I'm running two threads in the windows form:
public partial class Form1 : Form
{
Ticker ticker1 = new Ticker();
Ticker ticker2 = new Ticker();
Thread t;
Thread t1;
public Form1()
{
InitializeComponent();
ticker1.Tick += ticker1_Tick;
ticker2.Tick += ticker2_Tick;
t = new Thread(new ThreadStart(ticker1.TickIt));
t1 = new Thread(new ThreadStart(ticker2.TickIt)));
t.Start();
t1.Start();
}
public void ticker1_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
ticker1_Tick(sender, e);
});
return;
}
richTextBox1.Text += "t1 ";
}
public void ticker2_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
ticker2_Tick(sender, e);
});
return;
}
richTextBox2.Text += "t2 ";
}
The problem is after some seconds thread t is ahead of t1 by several ticks.
First of all why is this happening, it doesn't make sense, since each thread should wait 300 ms before ticking?
Second, how can I sync these two threads, so they tick simultaneously and one doesn't get ahead of the other?
I can't put a lock before the while loop, then only one thread will be running, while the other is locked out. Putting a lock elsewhere doesn't change anything.
If you really need them to be perfectly in synch and execute the ticks in a certain order, you will need some kind of central timer as Jaime mentioned. If you need independent timing but want to prevent drift caused by Sleep being imprecise, or delay added by the time it takes to execute the event handler, something like this would work:
public class Ticker
{
public event EventHandler Tick;
public EventArgs e = null;
public void TickIt()
{
const int targetSleepTime = 300;
int nextTick = Environment.TickCount + targetSleepTime;
while (true)
{
System.Threading.Thread.Sleep(Math.Max(nextTick - Environment.TickCount, 0));
if (Tick != null)
{
Tick(this, e);
}
nextTick += targetSleepTime;
}
}
}
Just keep in mind Environment.TickCount can wrap back to Int32.MinValue when it gets to Int32.MaxValue. You'll need extra code to handle that, or maybe base the timing on DateTime.UtcNow (less overhead than DateTime.Now).
I don't think you can trust the sleep(300) to keep your threads running the same number of times independently...
One thing you could do is to have a central timer/tick generator that signals a synchronization object on each tick, and the thread function only ticks once and then WaitsForObject for the next tick to be generated from the main thread, effectively having one timer and telling the threads to tick synchronously.
Also note that the way you are subscribing to the thread function event, you need to consider race conditions in your handler functions. Each method will run on it's own thread (until the begininvoke) so, if you access any resource (class fields etc.) those would need to be synchronized. It's just too easy to forget what's going on with the threads. :(
How about using AutoResetEvent?
class Program
{
static readonly AutoResetEvent thread1Step = new AutoResetEvent(false);
static readonly AutoResetEvent thread2Step = new AutoResetEvent(false);
static void Main(string[] args)
{
new Thread(new ThreadStart(Thread1Main)).Start();
new Thread(new ThreadStart(Thread2Main)).Start();
}
private static void Thread1Main()
{
for (int i = 0; i < int.MaxValue; i++)
{
Console.WriteLine("thread1 i=" + i);
thread1Step.Set();
thread2Step.WaitOne();
}
}
private static void Thread2Main()
{
for (int i = 0; i < int.MaxValue; i++)
{
Console.WriteLine("thread2 i=" + i);
thread2Step.Set();
thread1Step.WaitOne();
}
}
}
Well you could use a Barrier if you're using .NET 4.0, but you would have to put it in your Ticker class otherwise you'll block your UI thread.
http://msdn.microsoft.com/en-us/library/system.threading.barrier.aspx
In your Ticker class, increase your polling frequency and check the system timer until you hit the interval you're looking for. You can use TickCount or Ticks if you can live with millisecond precision, or use StopWatch for higher precision if your system supports it.
To keep them synchronized, they'll need a common reference for a start-time. You can pass this in as a specific future tick to start syncing on or use something like Tick modulus 100. Or poll for a shared static flag that signifies when to start.
You cannot have absolute precision, so define what precision range you can live with from the outset, such as plus-or-minus 5ms between your Ticker threads.
One thing that'll help is to start a shared static StopWatch instance and echo its elapsed time in all of your logging to help you characterize any delays in your app.

Categories

Resources