Search code examples
c#wpfmultithreadingbackgroundworker

Why is C# BackgroundWorker not reporting progress when importing a file?


I'm new to multithreading and I need help. I'm trying to use the BackgroundWorker in C# (WPF-codebehind) in order to update progress (through label content) whilst importing a file through a BinaryReader. The problem is that I need to have the full file in order to continue the execution of the program. Therefore, I need to know when it's finished.

I am trying to lock the main thread inside a while loop until a boolean flag is true, but the ProgressChanged and RunWorkerCompleted methods are never reached and the main thread seems to be locked inside the while loop after the bytes are read. Can you suggest what is the best approach in this case please? Here's my code below. Thanks!

Global Declarations:

List<byte> storage_list = new List<byte>();
byte[] storage_byte_array = { (byte)0 };
bool Completed_Flag = false;

Part of method to process imported file:

byte[] data = Read_File(directory_path);

foreach (byte b in data)
{
//etc

Read_File:

 byte[] Read_File(string directory_path)
 {
        Completed_Flag = false;
        BackgroundWorker worker = new BackgroundWorker();
        worker.WorkerReportsProgress = true;
        worker.DoWork += worker_DoWork;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        worker.RunWorkerAsync(directory_path);

        while (Completed_Flag == false)
        {
            System.Threading.Thread.Sleep(100);
        }
        return storage_byte_array;
   }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        storage_byte_array = storage_list.ToArray();
        Completed_Flag = true;
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        LBL.Content = e.ProgressPercentage;
        Console.WriteLine(e.ProgressPercentage);
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        string filename = (string)e.Argument;
        FileStream stream = File.OpenRead(@filename);
        var length = stream.Length;
        var position = 0;
        var buffer = new byte[1024];
        BinaryReader binReader = new BinaryReader(stream);

        while (position < length)
        {
            int store = binReader.Read(buffer, 0, buffer.Length);
            position = position + store;
            for (int i = 0; i < buffer.Length; i++)
            {
                storage_list.Add(buffer[i]);
            }
            int progressPercentage = Convert.ToInt32(((double)position / length) * 100);

            (sender as BackgroundWorker).ReportProgress(progressPercentage);
        }
        e.Result = storage_list;
    }

Solution

  • From the API:

    "The call to the ReportProgress method is asynchronous and returns immediately. The ProgressChanged event handler executes on the thread that created the BackgroundWorker."

    Presumably what is happening is that since you're busy-waiting on the Main thread here,

    while (Completed_Flag == false)
    {
       System.Threading.Thread.Sleep(100);
    }
    

    ... the call to ReportProgress() never gets a chance to execute. The way you've architected this, it's basically doing a synchronous operation anyway, so why bother with a BackgroundWorker at all?