Loading data async with queue. Proper write only locking and scheduling I have to store and return data on multitasking requests. If data is missing, I schedule loading and return null. I'm using some nubie scheduling by saving current jobs in a list. Also I need data request to be locked during writing. .NET 4.0

How can I schedule and lock properly?

private ConcurrentDictionary&lt;string, IDataSet&gt; data;
private List&lt;string&gt; loadingNow;

public IDataSet GetData(string dataId)
{
    if (loadingNow.Contains(dataId))
        return null; // Currently loading. Return null

    if (data.ContainsKey(dataId))
        return data[dataId]; // Return data

    // Schedule loading async. Return null.
    loadingNow.Add(dataId);
    dataIoAsync.LoadDataAsync(dataParams);
    return null;
}

private void DataIoAsync_DataFileLoaded(object sender, DataFileLoadedAsyncEventArgs e)
{
    loadingNow.Remove(e.DataId);
    data.TryAdd(e.DataId, e.DataSet);
    OnDataFileLoaded(e.DataId);
}


EDIT: Dictionary replaced with ConcurrentDictionary
Instead of keeping track of what's being loaded, you can represent that as a state by wrapping IDataSet into a class:

private sealed class DataSetResult
{
    public volatile IDataSet Result;
}


When an instance of this class is created, start loading the data. You can then just return the value of the Result field - if it is null, the data is still being loaded:

private readonly ConcurrentDictionary&lt;string, Lazy&lt;DataSetResult&gt;&gt; _data;

public IDataSet GetData(string dataId)
{
    var dataSet = _data.GetOrAdd(
        dataId, 
        _ =&gt; new Lazy&lt;DataSetResult&gt;(
            () =&gt;
            {
                var result = new DataSetResult();
                dataIoAsync.LoadDataAsync(dataParams);
                return result;
            }
        )
    );

    return dataSet.Value.Result;
}

private void DataIoAsync_DataFileLoaded(object sender, DataFileLoadedAsyncEventArgs e)
{
    _data[e.DataId].Value.Result = e.DataSet;
    OnDataFileLoaded(e.DataId);
}


The Lazy&lt;DataSetResult&gt; above ensures that data is retrieved at most once for a given id.

Personally, however, I would prefer to return Task&lt;IDataSet&gt; from this method, as it is really up to the caller to decide whether they want to wait for the result to become available or not. Using Task also allows you to report any exceptions to the client:

private readonly ConcurrentDictionary&lt;string, Lazy&lt;TaskCompletionSource&lt;IDataSet&gt;&gt;&gt; _data;

public Task&lt;IDataSet&gt; GetDataAsync(string dataId)
{
    var dataSet = _data.GetOrAdd(
        dataId, 
        _ =&gt; new Lazy&lt;TaskCompletionSource&lt;IDataSet&gt;&gt;(
            () =&gt;
            {
                var result = new TaskCompletionSource&lt;IDataSet&gt;();
                dataIoAsync.LoadDataAsync(dataParams);
                return result;
            }
        )
    );

    return dataSet.Value.Task;
}

private void DataIoAsync_DataFileLoaded(object sender, DataFileLoadedAsyncEventArgs e)
{
    _data[e.DataId].Value.TrySetResult(e.DataSet);
    OnDataFileLoaded(e.DataId);
}