Providing feedback during heavy calculation

M

Michiel Alsters

Hello everybody,

I hope anybody can help me. I'll try to give a brief overview of my
problem. I have running a program that performs a heavy calculation.
To give the user feedback what the program is doing I show a window
which contains a progress bar and a label. At some point during the
execution the state of the calculation is changed, so I want to let
the user know this.
I have placed the creation of the form in a seperate thread and
created a timer that checks every second if the status is changed.
This timer works good, but the form is not refreshed, i.e. it is not
showing the new status.
What is the problem here?
I have included a piece of the code (see below).

Thanks for your time and help,

Michiel

public class CalcThread
{
private int counter;
private int calcStatus;
private System.Threading.Timer tmrCalc;
private frmWait frmCalc;

// Constructor accepts "parameters" meant for the ThreadStart
delegate.
public CalcThread()
{
counter = 0;
calcStatus = 0;
}

// ThreadStart delegate is a parameterless method of the threaded
class.
public void Launch()
{
/* Create a wait form */
frmCalc = new frmWait();
frmCalc.setStateText(calcStatus);
frmCalc.Show();
frmCalc.Refresh();

/* Create the delegate that invokes methods for the timer */
TimerCallback timerDelegate = new TimerCallback(CheckStatus);

/* Create a timer that waits one second, then invokes every second
*/
System.Threading.Timer timer = new
System.Threading.Timer(timerDelegate, null, 1000, 1000);

/* Keep a handle to the timer, so it can be disposed */
tmrCalc = timer;
counter = 0;
}

public void Abort()
{
tmrCalc.Dispose();
tmrCalc = null;
}

public void Update(int status)
{
calcStatus = status;
}

public int CalcTime()
{
return counter;
}

private void CheckStatus(Object state)
{
counter++;
frmCalc.setStateText(calcStatus);
frmCalc.setCalcText();
frmCalc.Refresh();
Application.DoEvents();
Console.WriteLine("{0} Checking Status {1}.",
DateTime.Now.TimeOfDay, counter);
}
}

/* The application */
private void btnCalculate_Click(object sender, System.EventArgs e)
{
/* show calculation message */
calcStatus = 0;
CalcThread calcThread = new CalcThread();
thWait = new Thread(new ThreadStart(calcThread.Launch));
thWait.Start();

// The heavy calculation

calcThread.Update(++calcStatus);

// More heavy calculation

calc_time = calcThread.CalcTime();
calcThread.Abort();
thWait.Abort();
}
 
J

Jack Mayhoff [MSFT]

Cant you register a callback? IF its lengthy, stick it in a thread and have
the caller register a callback or data or signal an event.

--

Jack Mayhoff
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
J

Jon Skeet [C# MVP]

Michiel Alsters said:
I hope anybody can help me. I'll try to give a brief overview of my
problem. I have running a program that performs a heavy calculation.
To give the user feedback what the program is doing I show a window
which contains a progress bar and a label. At some point during the
execution the state of the calculation is changed, so I want to let
the user know this.
I have placed the creation of the form in a seperate thread and
created a timer that checks every second if the status is changed.
This timer works good, but the form is not refreshed, i.e. it is not
showing the new status.

Rather than creating a timer to check the status, you should have your
calculation thread update the GUI periodically. You'll need to use
Control.Invoke to make sure the GUI is updated in the appropriate
thread.
 
F

Fred Mellender

I'm not sure, but I think the problem might be that your "wait form" is not
created on the UI thread. I have a very similar application that works
well, but I designed it as follows:

A. Use an item on the UI form itself (vi the MDE Form designer), like a
progress bar, or text box, that will
display calculations/progress from the calculator thread.

B. Put a System.Windows.Forms.Timer on the UI form too (via the MDE Forms
designer's "toolbox"). I suppose a Threading.Timer would work as well,
but it is easier to let
the forms designer generate the timer's callback.

C. When the user Clicks the button to start the calculation:
1. Start the timer.
2. Start the long calculation's thread.

D. In the callback for the timer click (generated by the forms designer in
the MDE):
stop the timer.
lock the calculation thread's counter.
display the counter.
unlock the counter.
start the timer.

Be sure to lock the counter in the thread that does the long calculation
before you update it in that thread.

I believe the logic is about the same that you have, except the "progress
form" is just an item on the UI's form, the timer is a Forms.Timer, there is
no separate thread for updating the progress counter, but it is done on the
UI thread. Also, there is no need for the Application.DoEvents();

As others have suggested, it is sometimes better to let the long calculation
create an event to update UI. But your design has the advantage that the UI
is updated after a constant time interval, which is a bit more pleasing to
the eye.
 
M

Michiel Alsters

Thanks, this helped a lot. I finally put both the calculation and the
message form in a seperate thread. This was the trick. This way I
could, as you sugested, suspend the calculation thread and update the
UI.

Thanks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top