Odd Behaviour??

  • Thread starter Thread starter John Baro
  • Start date Start date
J

John Baro

I have an app that spawns a thread to copy some files.
The spawned thread then spawns threads to copy individual files and wait for
the specific file to finish copying or timeout.
The specific file copy procedure raises an event which gets handled in the
main form from where the original thread was spawned which then updates a
status box and a progress bar.
The first spawned thread raises a startcopy event which the main form
handles to display the progress bar and a finishcopy event to hide the
progress bar.
This was all working peachy until I moved the individual file copying to
individual threads. After that It was REALLY slow and the progress bar would
not display.
When I set the visibility of the progress bar to TRUE to begin with it works
fine. Even after the progress bar has been hidden by the finishcopy event
and shown again by the startcopy event if I run the routine again.

Now for the question.
Why is this so?

I apologize for the long winded explanation but I really dont understand why
this is.

TIA
JB

P.S. The main form "Freezes" when the progress bar is not visible to begin
with. It wont allow me to resize or do anything whilst the filecopying is
going on, even though it is being run in another thread.
 
Maybe you have too many threads copying files at once causing your hdd to thrash. One thread might be better.
 
My guess: if you look at the number of drive faults you are getting, you
will see that you are thrashing.

In other words, your processor asks for a disk sector and starts reading.
Optimized files will occur with contiguous sectors on the drive (therefore,
as soon as the system is done reading the first sector, the next sector in
line is just coming into range as the head passes over the platter. In the
mean time, thread context switches to another thread, which asks for a
different sector. The first sector is read, but not the all contiguous
sectors. The hard drive seeks to the second location and starts reading
sectors. At this point, the thread context switches, and in your
application, the hard drive goes off again without finishing the optimal
task (it ALWAYS will... hard drive reads are slow, and the processor looks
for something else to do).

This is wildly inefficient. If you have one thread reading a file, it will
read all of the continguous sectors before seeking the hard drive to the
next location. If you have multiple threads reading multiple files, you
will have to seek to the same relative location many times just to complete.
Considering the fact that seeking from sector to sector is the biggest waste
of time in hard drive manipulations, you are NOT better off by
multi-threading an application that hits one hard drive.

This gets worse because you are probably writing to the same drive too,
right?

This phenomenom is called hard drive thrashing, and is similar to OS
thrashing (where so many applications are loaded at the same time that the
OS spends more time performing page faults to switch from one context to
another than it does actually doing any of the work of the applications).

Now, if you were copying files from multiple locations to multiple
destinations, I can see creating multiple threads. But one source to the
same destination is dicey enough without overwhelming it with seeks.

I hope that helps,
--- Nick Malik
Solutions Architect
 
Thanks for the answer.

I knew I should have written this when I was awake.

I only spawn 1 thread to copy all files which in turn spawns 1 thread to
copy 1 file at a time. This is done so that I can have fault tolerance and
cancel the copy if it is hanging on copying a file.
This occurrs when a file is damaged. I wrote this to copy files off a
damaged CD and ignore damaged files.

The most important part..
When the progress bar is set to visible before I start copying files (In the
IDE), then it works perfectly.
No disk thrashing is occurring, guaranteed.
I can email you the project if you want.
JB

Michael Culley said:
Maybe you have too many threads copying files at once causing your hdd to
thrash. One thread might be better.
 
Hi John,
You raise the event from the worker thread. The event handler, which is
method of the form class, is executed by this worker thread. Is it posible
that you don't call Control.Invoke and manipulate the progress bar from the
worker thread. If you do so you can exepect any strange behaviour. You know
that you can use controls only from the UI thread that has created them.
 
Hi Stoitcho

Thanks for the reply
The only references to pbStatus (the progress bar) are below.
I just checked again and it works exactly as I said.
When I have the progress bar hidden to start with, it does not draw properly
and when I tried to maximise the form it froze the app and I had to kill it.
It is so very odd.
private void fileCopy_CopyProgress(object sender, CopyProgressEventArgs e)

{

txtResults.Text = e.Message + "\r\n" + e.SourceFolder + "\\" + e.Filename +
"\r\n" + txtResults.Text;

//really need to get or make a three segment scrolling progress bar like the
xp startup one

pbStatus.Value = pbStatus.Value == pbStatus.Maximum ? pbStatus.Minimum :
pbStatus.Value;

pbStatus.Value ++;

}

private void fileCopy_StartCopy(object sender, EventArgs e)

{

pbStatus.Visible = true;

}

private void fileCopy_FinishCopy(object sender, EventArgs e)

{

pbStatus.Visible = false;

}

}
 
John Baro said:
Hi Stoitcho

Thanks for the reply
The only references to pbStatus (the progress bar) are below.
I just checked again and it works exactly as I said.
When I have the progress bar hidden to start with, it does not draw properly
and when I tried to maximise the form it froze the app and I had to kill it.
It is so very odd.

Odd things are bound to happen if you access controls from threads other
than the UI thread. As Stoitcho already suggested, use Control.Invoke to
access methods or properties of a control from other threads. Otherwise it
will never work reliably.

Sami
www.capehill.net
 
Hi John,
Obviously you don't use Control.Invoke and you try to manipulate the
progress bar from the worker thread. This is your problem I believe. The
following is what you might wanna do (see my suggestions inlined in your
code).

//Probably you have this already defined in your application
delegate void CopyProgressEventHandler(object sender, CopyProgressEventArgs
e);

private void fileCopy_CopyProgress(object sender, CopyProgressEventArgs e)
//You don't have to do this for all controls you want to change. In fact
you can use this.InvokeRequired and this.Invoke if the event handler is form
class method
//Since this event handler might be frequently called you may create a
CopyProgressEventHandler delegate
in the constructor of the form and reuse it in order not to pollute the
heap with objects each time the event is fired
if(pbStatus.InvokeRequired)
{
pbStatus.Invoke(new CopyProgressEventHandler(fileCopy_CopyProgress),
new object[]{sender, e});
return;
}
txtResults.Text = e.Message + "\r\n" + e.SourceFolder + "\\" + e.Filename +
"\r\n" + txtResults.Text;

//really need to get or make a three segment scrolling progress bar like the
xp startup one

pbStatus.Value = pbStatus.Value == pbStatus.Maximum ? pbStatus.Minimum :
pbStatus.Value;

pbStatus.Value ++;

}

private void fileCopy_StartCopy(object sender, EventArgs e)

{
if(pbStatus.InvokeRequired)
{
pbStatus.Invoke(new EventHandler(fileCopy_StartCopy), new
object[]{sender, e});
return;
}
pbStatus.Visible = true;

}

private void fileCopy_FinishCopy(object sender, EventArgs e)

{
if(pbStatus.InvokeRequired)
{
pbStatus.Invoke(new EventHandler(fileCopy_FinishCopy), new
object[]{sender, e});
return;
}
 
Thanks Sami
Sami Vaaraniemi said:
Odd things are bound to happen if you access controls from threads other
than the UI thread. As Stoitcho already suggested, use Control.Invoke to
access methods or properties of a control from other threads. Otherwise it
will never work reliably.

Sami
www.capehill.net
 
Thanks Stoitcho
Obviously you don't use Control.Invoke and you try to manipulate the
progress bar from the worker thread. This is your problem I believe.
I hadnt thought of that.
What you are saying is that the event raised from the filecopy method runs
in the same thread that it was raised in?
I hadnt even thought about this. This is my first foray into multithreaded
applications. :)
Thanks heaps for your help. I will look into control.invoke and read up on
it to better understand.

Cheers
JB
 
Hi John,
I hadnt thought of that.
What you are saying is that the event raised from the filecopy method runs
in the same thread that it was raised in?

Yes, ofcourse. It follows the execution thread.
I hadnt even thought about this. This is my first foray into multithreaded
applications. :)

:-) it has its oddity
Thanks heaps for your help. I will look into control.invoke and read up on
it to better understand.

Ok. Good luck. :-) Post a message if it solves your problem.
 
Back
Top