views:

106

answers:

3

How can i efficiently display the status of a file when using a background thread?

For instance, lets say i have a 100MB file:

when i do the code below via a thread (just as an example) it runs in about 1 min:

foreach(byte b in file.bytes)
{
   WriteByte(b, xxx);
}

But... if i want to update the user i have to use a delegate to update the UI from the main thread, the code below takes - FOREVER - literally i don't know how long im still waiting, ive created this post and its not even 30% done.

int total = file.length;
int current = 0;
foreach(byte b in file.bytes)
{
   current++;
   UpdateCurrentFileStatus(current, total);
   WriteByte(b, xxx);
}

public delegate void UpdateCurrentFileStatus(int cur, int total);
public void UpdateCurrentFileStatus(int cur, int total)
{
        // Check if invoke required, if so create instance of delegate
        // the update the UI

        if(this.InvokeRequired)
        {

        }
        else
        {
          UpdateUI(...)
        }
}
+6  A: 

Dont update the UI on every byte. only update it for every 100k or so.

Observe:

int total = file.length;
int current = 0;
foreach(byte b in file.bytes)
{
   current++;
   if (current % 100000 == 0)
   {
        UpdateCurrentFileStatus(current, total);
   }
   WriteByte(b, xxx);
}
Byron Whitlock
very odd, i was thinking exactly that before i went back to this page - i'll give that a shot and see...I mean even every 1k bytes...
schmoopy
Also use `Control.BeginInvoke` instead of `Invoke`, so the worker thread doesn't have to wait for the UI thread.
Ben Voigt
+2  A: 

You're updating the UI too frequently -- every byte in a 100MB file is going to result in 100 million UI updates (marshalling to the UI thread for each).

Break your updates up into percentages of the total file size, maybe 10% or even 5% increments. So if the file size was 100 bytes, update the UI at 10, 20, 30, etc.

Ragoczy
+1  A: 

I recommend that you update according to elapsed time so that you have predictable update intervals regardless of file size or system load:

    DateTime start = DateTime.Now;
    foreach (byte b in file.bytes)
    {
        if ((DateTime.Now - start).TotalMilliseconds >= 200)
        {
            UpdateCurrentFileStatus(current, total);
            start = DateTime.Now;
        }
    }
ebpower
For relative time measurements in a loop, it's much better to use `Stopwatch` or even `DateTime.UtcNow`, which both are much much faster than `DateTime.Now`.
Ben Voigt
Good point. I also didn't realize that DateTime.UtcNow is faster (no timezone conversion). It's always a treat to learn something from answering something else!You could also use a System.Timer to update the UI, especially if the work is done on another thread.
ebpower
I like this approach the best so far, thank you ebpower
schmoopy