From apps I've written and one I've inherited, I have a continuing desire to better understand the thread-safety issues of loading data on a background thread. Suppose I have a simple, single-window Windows Forms app with a "Load" button and a BackgroundWorker:

The button's Click handler calls loadBackgroundWorker.RunWorkerAsync(), and the worker's DoWork handler creates and initializes an object of type Document which, after loading, it stores in the form's LoadedDocument property. In the worker's RunWorkerCompleted handler, a MessageBox displays the properties of the LoadedDocument. I know this is all hard to visualize, so I'm including complete code. Sorry that it makes the question so long to read.
Here's the form's code:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace BackgroundLoadTest
{
public partial class Form1 : Form
{
private Document _loadedDocument;
public Document LoadedDocument
{
get
{
lock (this)
{
return _loadedDocument;
}
}
set
{
lock (this)
{
_loadedDocument = value;
}
}
}
public Form1()
{
InitializeComponent();
loadBackgroundWorker.DoWork += new DoWorkEventHandler(loadBackgroundWorker_DoWork);
loadBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(loadBackgroundWorker_RunWorkerCompleted);
}
void loadBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Document d = new Document();
d.Property1 = "Testing";
d.Property2 = 1;
d.Property3 = 2;
this.LoadedDocument = d;
}
void loadBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Document loaded with Property1 = " +
LoadedDocument.Property1 + ", Property2 = " +
LoadedDocument.Property2 + ", Property3 = " +
LoadedDocument.Property3);
}
private void loadButton_Click(object sender, EventArgs e)
{
loadBackgroundWorker.RunWorkerAsync();
}
}
}
Here's the code for the Document class:
using System;
namespace BackgroundLoadTest
{
public class Document
{
public string Property1 { get; set; }
public double Property2 { get; set; }
public int Property3 { get; set; }
}
}
My question is:
What thread-safety/memory-visibility problems do you see with this code, or what would you do differently given the goal of loading data on the background thread and eventually using the loaded data on the UI thread?
Is the locking in the LoadedDocument property sufficient to ensure that data initialized in the background thread will be visible to the UI thread? Is the locking necessary? I really want to understand the seemingly very common problem of loading complex documents on a background thread while keeping the GUI responsive, and I know it's tricky stuff.
Edit: to be clear, what I'm most concerned about here is memory visibility. I want to be sure that all the data initialization done by the background thread becomes visible to the GUI thread when the worker completes. I don't want changes getting stuck in a CPU cache and remaining invisible to threads on other CPUs. I don't know how to state my concerns better because they're still rather vague to me.