In MVVM, how can I prevent BackgroundWorker from freezing UI?
<p>First of all, I've seen many similar issues here on SO and around the net.  None of these appear to address my particular issue.</p>

<p>I have a simple <code>BackgroundWorker</code> whose job is to read a file line-by-line and report progress to indicate how far through it is.  There are up to a total of 65,553 lines in the file, so it is important to me that the <code>BackgroundWorker</code> finishes as fast as possible.</p>

<p>Since MVVM is built on <a href="https://en.wikipedia.org/wiki/Separation_of_concerns" rel="nofollow noreferrer">separation of concerns (SoC)</a> and the decoupling of the View and View-Model, the <code>BackgroundWorker</code> updates properties on the View-Model that the View binds to.  My setup is very similar to <a href="https://stackoverflow.com/a/3520436/975724">Kent Boorgaart's answer</a> on another question.</p>

<p>In high-stress scenarios where the <code>BackgroundWorker</code> demands a lot of CPU without sleeping, the UI thread is starved and not able to update any of the bound properties that have been notified via <code>INotifyPropertyChanged</code>.  However, if the <code>BackgroundWorker</code> sleeps, then the job will not finish as fast as possible.</p>

<h2>How can I ensure the View receives progress updates while respecting MVVM and while not throttling the job?</h2>

<p>In the View-Model the <code>BackgroundWorker</code> is setup like this.  The <code>Start()</code> function is called by a <code>RelayCommand</code> (part of <a href="https://mvvmlight.codeplex.com/" rel="nofollow noreferrer">MVVM-Light</a>).</p>

<pre><code>public void Start(string memoryFile)
{
    this.memoryFile = memoryFile;
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += Worker_DoWork;
    worker.ProgressChanged += Worker_ProgressChanged;
    worker.WorkerReportsProgress = true;
    worker.RunWorkerAsync();
}
</code></pre>

<p>Here is the code for the actual work performed:</p>

<pre><code>private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker bw = (BackgroundWorker)sender;
    IsAnalyzing = true;
    bw.ReportProgress(0, new ProgressState("Processing..."));

    int count = File.ReadLines(memoryFile).Count();
    StreamReader reader = new StreamReader(memoryFile);
    string line = "";
    int lineIndex = 0;
    while ((line = reader.ReadLine()) != null)
    {
        bw.ReportProgress((int)(((double)lineIndex / count) * 100.0d));

        //Process record... (assume time consuming operation)
        HexRecord record = HexFileUtil.ParseLine(line);

        lineIndex++;
        if (lineIndex % 150 == 0)
        {
            //Uncomment to give UI thread some time.
            //However, this will throttle the job.
            //Thread.Sleep(5);
        }
    }
    bw.ReportProgress(100, new ProgressState("Done."));

    Thread.Sleep(1000);
    IsAnalyzing = false;
}

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    Progress = e.ProgressPercentage;
    if (e.UserState != null)
    {
        Task = ((ProgressState)e.UserState).Task;
    }
}
</code></pre>

<p>In the above code, the following properties are used for bindings between the View and View-Model and each will trigger <a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged(v=vs.110).aspx" rel="nofollow noreferrer"><code>INotifyPropertyChanged.PropertyChanged</code></a> events:</p>

<ul>
<li><code>Progress</code></li>
<li><code>Task</code></li>
<li><code>IsAnalyzing</code></li>
</ul>

<p><hr></p>

<h3>EDIT:</h3>

<p>In follow up Stephen Cleary and Filip Cordas, I've attempted using <code>Task.Run()</code> with and without <code>ObservableProgress</code>.</p>

<p>I've simplified the background task to iterate through numbers instead of lines of a file.</p>

<pre><code>private void DoWork(IProgress&lt;ProgressState&gt; progress)
{
    IsAnalyzing = true;
    progress.Report(new ProgressState(0, "Processing..."));

    for (int i = 0; i &lt; 2000000000; i += 1000000)
    {
        int percent = (int)(((double)i / 2000000000) * 100.0d);
        progress.Report(new ProgressState(percent, String.Format("Processing ({0}%)", percent)));
        Thread.Sleep(5);
    }
    progress.Report(new ProgressState(100, "Done."));

    Thread.Sleep(1000);
    IsAnalyzing = false;
}
</code></pre>

<p>Now, I start the task in either one or two ways (with or without the <code>ObservableProgress</code>):</p>

<pre><code>public void Start(string memoryFile)
{
    this.memoryFile = memoryFile;

    /* TODO: Uncomment this section to use ObservableProgress instead.
     ObservableProgress.CreateAsync&lt;ProgressState&gt;(progress =&gt; System.Threading.Tasks.Task.Run(() =&gt; DoWork(progress)))
    .Sample(TimeSpan.FromMilliseconds(50))
    .ObserveOn(Application.Current.Dispatcher)
    .Subscribe(p =&gt;
    {
        Progress = p.ProgressPercentage;
        Task = p.Task;
    });*/

    // TODO: Comment this section to use ObservableProgress instead.
    var progress = new Progress&lt;ProgressState&gt;();
    progress.ProgressChanged += (s, p) =&gt;
    {
        Progress = p.ProgressPercentage;
        Task = p.Task;
    };
    System.Threading.Tasks.Task.Run(() =&gt; DoWork(progress));
}
</code></pre>

<p>
<strong>ObservableProgress.cs</strong></p>

<pre><code>public static class ObservableProgress
{
    public static IObservable&lt;T&gt; CreateAsync&lt;T&gt;(Func&lt;IProgress&lt;T&gt;, Task&gt; action)
    {
        return Observable.Create&lt;T&gt;(async obs =&gt;
        {
            await action(new Progress&lt;T&gt;(obs.OnNext));
            obs.OnCompleted();

            return Disposable.Empty;
        });
    }
}
</code></pre>

<p>In both scenarios (with or without <code>ObservableProgress</code>) I find that I still need to throttle the background job by using <code>Thread.Sleep(5)</code>.  Otherwise the UI freezes.</p>

<p><hr></p>

<h3>EDIT 2:</h3>

<p>I made a small modification to progress reports inside the worker thread:</p>

<pre><code>for (int i = 0; i &lt; 2000000000; i += 10) //Notice this loop iterates a lot more.
{
    int percent = (int)(((double)i / 2000000000) * 100.0d);
    //Thread.Sleep(5); //NOT Throttling anymore.
    if (i % 1000000 == 0)
    {
        progress.Report(new ProgressState(percent, String.Format("Processing ({0}%)", percent)));
    }
}
</code></pre>

<p>With this modification, the UI does not lock anymore and changes are propagating properly.  Why is this so?</p>