Multithreading, timer and form updating

J

JL

Hi all,

I have a problem that I can't get rid off, and after many searches on the
Net I finally need your help! It seems like a basic multithreading thing,
but it's not as simple.
Let me explain the thing : I use a method with long time calculation. This
method comes from a SDK so I can't modify the code. My aim is to launch
this method, show a form that display the elapsed time, and enable the user
to use the mainform during the calculation. The problme I'm facing is that
the main form doesn't redraw during the calculation.
I tried several things :
- using a backgroundworker for the long method and a thread for the "elapsed
time form" with a timer within. The "elapsed time form" starts a timer that
forces the redraw with the new time --> if I use ElapsedTimeForm.Show the
"elapsed time form" isn't redrawn. If i use ElapsedTimeForm.ShowDialog the
"elapsed time form" is redrawn but the mainform is freezed (modal dialog,
normal).
- using a backgroundworker for the long method and a System.Threading.Timer
that would change the main form text with the elapsed time --> the mainform
isn't redrawn during the calculation.
- several crappy things :)

My main issue is that I can't use a backgroundwork with the reportprogress
and all stuff because my calculation method is "in one block", no way to
stop, pause, or do something from this method.
I have read many articles on multithreading in C#, and I really can't find
the good way to do this.

Anyone can help ?

Thanks !
 
G

Guest

JL said:
Hi all,

I have a problem that I can't get rid off, and after many searches on the
Net I finally need your help! It seems like a basic multithreading thing,
but it's not as simple.
Let me explain the thing : I use a method with long time calculation. This
method comes from a SDK so I can't modify the code. My aim is to launch
this method, show a form that display the elapsed time, and enable the user
to use the mainform during the calculation. The problme I'm facing is that
the main form doesn't redraw during the calculation.
I tried several things :
- using a backgroundworker for the long method and a thread for the "elapsed
time form" with a timer within. The "elapsed time form" starts a timer that
forces the redraw with the new time --> if I use ElapsedTimeForm.Show the
"elapsed time form" isn't redrawn. If i use ElapsedTimeForm.ShowDialog the
"elapsed time form" is redrawn but the mainform is freezed (modal dialog,
normal).
- using a backgroundworker for the long method and a System.Threading.Timer
that would change the main form text with the elapsed time --> the mainform
isn't redrawn during the calculation.
- several crappy things :)

My main issue is that I can't use a backgroundwork with the reportprogress
and all stuff because my calculation method is "in one block", no way to
stop, pause, or do something from this method.
I have read many articles on multithreading in C#, and I really can't find
the good way to do this.

Anyone can help ?

Thanks !

Generaly you have to be aware of, that the modification of the GUI over an
other thread is requires Invoke.
If you have a thread which is doing the calculation, and this requires all
the CPU performance, you could set the Thread priority to "Priority =
ThreadPriority.BelowNormal".
But if you whant to give a graphical state on an other Form, this will also
block the GUI.

Hope it helps!

All the best,

Martin
 
J

JL

Thank you for your advices. I do use the InvokeRequired and Invoke
mechanism.
I've tried to lower the priority of the calculation thread, but my main form
still doesn't refresh (I use invalidate).
I really don't know what to do ...
 
G

Guest

Like I said before:There seems not to be a solution for that!
At least if you don't whant to skip the additional information Window.
 
P

Peter Duniho

Thank you for your advices. I do use the InvokeRequired and Invoke
mechanism.
I've tried to lower the priority of the calculation thread, but my main
form
still doesn't refresh (I use invalidate).

The thread priority is unlikely to be relevant at all. Don't worry about
that.
 
P

Peter Duniho

[...]
My main issue is that I can't use a backgroundwork with the
reportprogress
and all stuff because my calculation method is "in one block", no way to
stop, pause, or do something from this method.

I understand you can't take advantage of the ReportProgress in
BackgroundWorker. But that doesn't mean you can't use BackgroundWorker.
It just means there's no way to report progress. So, that's not actually
your main issue at all.

As for what your main issue actually is...

The goal is to run some code on a thread, while not blocking the main UI..
At the same time, you want the main UI to show elapsed time.

The way to achieve that goal is to use a thread to run your processing
(BackgroundWorker is fine for this purpose), but to maintain the elapsed
time UI on the main thread. The key here is to not create the elapsed
time form on the worker thread, but rather from the main thread. As long
as you do that, it should work. For example:

class MainForm : Form
{
void StartLongProcessing()
{
ElapsedForm elapsed = new ElapsedForm();

elapsed.Closed += EndLongProcessing;
elapsed.StartLongProcessing(LongProcess);
}

void EndLongProcessing(object sender, EventArgs e)
{
// do whatever takes place after processing is done here
}

void LongProcess(object sender, DoWorkEventArgs e)
{
// do your long processing here
}
}

class ElapsedForm : Form
{
DateTime _dtStart;
System.Windows.Forms.Timer _timer = new
System.Windows.Forms.Timer();

public void StartLongProcessing(DoWorkEventHandler handler)
{
BackgroundWorker bw = new BackgroundWorker();

bw.DoWork += handler;
bw.RunWorkerCompleted += EndLongProcessing;

_dtStart = DateTime.Now;
_timer.Interval = 1000; // one-second intervals
_timer.Tick += UpdateElapsed;

bw.RunWorkerAsync();
_timer.Start();

Show();
}

void EndLongProcessing(object sender, RunWorkerCompletedEventArgs
e)
{
_timer.Stop();
Close();
}

void UpdateElapsed(object sender, EventArgs e)
{
labelElapsedTime.Text = (DateTime.Now - _dtStart).ToString();
}
}
 
W

Willy Denoyette [MVP]

JL said:
Thank you for your advices. I do use the InvokeRequired and Invoke
mechanism.
I've tried to lower the priority of the calculation thread, but my main
form still doesn't refresh (I use invalidate).
I really don't know what to do ...


This is quite normal if you are running this on a single CPU box, in that
case you should not use a background worker to update the UI, simply use
System.Windows.Forms.Timer on the UI thread to show progress.

Willy.
 
J

JL

Thank you Peter for this clear example.

Unfortunatly I exactly followed it but my ElapsedForm remains freezed... I
really don't understand.
The bad point for me is that I tried a simple backgroundworker to run my
processing (no ElapsedForm, just a BW to try to keep the UI alive). But when
my BW is runned I'm not able to use the UI anymore, even with the
multithread mechanism !

I've sent an email to the support of the SDK to know if there was something
"special" with this processing method (in the SDK there are several release
note about multithreading and C++, nothing about C#).




"Peter Duniho" <[email protected]> a écrit dans le message de
news: (e-mail address removed)...
[...]
My main issue is that I can't use a backgroundwork with the
reportprogress
and all stuff because my calculation method is "in one block", no way to
stop, pause, or do something from this method.

I understand you can't take advantage of the ReportProgress in
BackgroundWorker. But that doesn't mean you can't use BackgroundWorker.
It just means there's no way to report progress. So, that's not actually
your main issue at all.

As for what your main issue actually is...

The goal is to run some code on a thread, while not blocking the main UI.
At the same time, you want the main UI to show elapsed time.

The way to achieve that goal is to use a thread to run your processing
(BackgroundWorker is fine for this purpose), but to maintain the elapsed
time UI on the main thread. The key here is to not create the elapsed
time form on the worker thread, but rather from the main thread. As long
as you do that, it should work. For example:

class MainForm : Form
{
void StartLongProcessing()
{
ElapsedForm elapsed = new ElapsedForm();

elapsed.Closed += EndLongProcessing;
elapsed.StartLongProcessing(LongProcess);
}

void EndLongProcessing(object sender, EventArgs e)
{
// do whatever takes place after processing is done here
}

void LongProcess(object sender, DoWorkEventArgs e)
{
// do your long processing here
}
}

class ElapsedForm : Form
{
DateTime _dtStart;
System.Windows.Forms.Timer _timer = new
System.Windows.Forms.Timer();

public void StartLongProcessing(DoWorkEventHandler handler)
{
BackgroundWorker bw = new BackgroundWorker();

bw.DoWork += handler;
bw.RunWorkerCompleted += EndLongProcessing;

_dtStart = DateTime.Now;
_timer.Interval = 1000; // one-second intervals
_timer.Tick += UpdateElapsed;

bw.RunWorkerAsync();
_timer.Start();

Show();
}

void EndLongProcessing(object sender, RunWorkerCompletedEventArgs
e)
{
_timer.Stop();
Close();
}

void UpdateElapsed(object sender, EventArgs e)
{
labelElapsedTime.Text = (DateTime.Now - _dtStart).ToString();
}
}
 
P

Peter Duniho

Thank you Peter for this clear example.

Unfortunatly I exactly followed it but my ElapsedForm remains freezed...I
really don't understand.

Then you are doing something in your code that you have not mentioned here.

The code I posted works fine. You simply have to have two different forms
(MainForm and ElapsedForm) in your project. For testing purposes, put a
Thread.Sleep(10000) or something in the LongProcess() method. Run it, and
you will see it work, assuming you start with a clean project with nothing
else in it.
The bad point for me is that I tried a simple backgroundworker to run my
processing (no ElapsedForm, just a BW to try to keep the UI alive). But
when
my BW is runned I'm not able to use the UI anymore, even with the
multithread mechanism !

Then obviously the problem you're having is either not at all related to
the background processing, or you have some other issue that you haven't
explained.

In this case, the usual suggestion and the one that is definitely
applicable to you is: post a concise-but-complete example of code that
reliably reproduces the problem. Make sure you reduce the code to the
minimal code required; don't include anything that isn't directly needed
in order to reproduce the problem.

Note that in creating such an example of code, you may well find that
removing some particular part of the code causes it to work as expected.
When this happens, you are very close to a solution and may not need to
post the code here at all, if you can then use that information to figure
out the bug yourself.
I've sent an email to the support of the SDK to know if there was
something
"special" with this processing method (in the SDK there are several
release
note about multithreading and C++, nothing about C#).

I'm not sure what you mean here. If you are suggesting that this is
caused by a bug in the .NET Framework, I find that highly unlikely.

Pete
 
J

JL

Well, following your advice I replaced the processing method by a simple
Thread.Sleep and it works perfectly.
The problem seems to come from the processing method. As I explained this
method comes from a third company SDK.
That's why I talked about contacting the support of this company, because I
now have doubts about the multithreading capabilities of their SDK !
(Of course, I never meant there was a bug in the .net FW threading library).

Once again, thank you for your help Peter.
 
P

Peter Duniho

Well, following your advice I replaced the processing method by a simple
Thread.Sleep and it works perfectly.
The problem seems to come from the processing method. As I explained this
method comes from a third company SDK.
That's why I talked about contacting the support of this company,
because I
now have doubts about the multithreading capabilities of their SDK !

Ah, okay. I thought by SDK you meant the .NET Framework.

Yes, it sounds as though they've got something in their own code that's
interfering. What that might be, I don't know. It seems to me that you
would have to provide the library with some sort of reference to your main
thread for it to interfere with that main thread (e.g. a Control instance
owned by that thread, or something like that). But it would be hard to
say without more details.

Hope you can get them to fix it. :)

Pete
 
J

JL

Hi Peter,

I finally had the key of the problem with the support team of the SDK. They
admit that the long processing method won't give the hand to the GUI back
when runned.
The only way to do the thing with threads is to use a "Run - pause - resume
method" mechanism.I find it really bad !

Anyway, thank you for your help and your good advices!

JL
 
P

Peter Duniho

Hi Peter,

I finally had the key of the problem with the support team of the SDK.
They
admit that the long processing method won't give the hand to the GUI back
when runned.

Um, okay. But that leaves open the question as to how and why their SDK
gets access to your GUI thread and somehow takes it over.

If their SDK was actually independent of your GUI, it should not be able
to affect the GUI thread at all. So it's not independent. If you can
find out why it's not independent (or maybe you already know), that may
lead to a solution that changes that situation, so that it _is_
independent (for example, maybe if it for some reason needs a thread with
a message pump on it, give it a different thread with a message pump
specifically for its own use, so that it doesn't interfere with your GUI).

Just a thought.
The only way to do the thing with threads is to use a "Run - pause -
resume
method" mechanism.I find it really bad !

I agree. :)
Anyway, thank you for your help and your good advices!

You're very welcome. Glad it was of some help.

Pete
 

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