Я думаю, что ваша инкапсуляция может быть немного подправлена. Например, я думаю, что вы должны разделить код, который асинхронно получает данные, на следующее:
static class DataFactory
{
internal static DataType GetData()
{
// Return the data.
return new DataType();
}
}
Тогда ваш экземпляр класса может отдельно беспокоиться о состоянии и использовать Task<T>
для облегчения этого:
class DataManager
{
// The lock on the operation.
private readonly object lockObj = new object();
// The state.
private State theState = State.None;
// The task to get the state.
private Task<DataType> getDataTask;
public DataType GetDataAsync()
{
lock(lockObj)
{
if (theState == State.Getting)
return null;
if (theState == State.Complete
return getDataTask.Result;
// Set the state to getting.
theState = State.Getting;
// Start the task.
getDataTask = Task.Factory.StartNew(() => {
// Get the data.
DataType result = DataFactory.GetData();
// Lock and set the state.
lock (lockObj)
{
// Set the state.
theState = State.Complete;
}
// Return the result.
return result;
});
// Return null to indicate the operation started
// (is in "getting" state).
return null;
}
}
}
Теперь, поскольку вы используете Task<T>
, ваш GetDataBlocking
(я думаю, его следует назвать GetData
) метод становится очень простым:
public DataType GetData()
{
// Get the data async, if the result is non null, then
// return it.
DataType result = GetDataAsync();
// If it is non-null, return it.
if (result != null) return result;
// If at this point, the operation has been kicked off
// to load the data. Just wait on the task and return the result then.
getDataTask.Wait();
// Return the async data again, it will just return the
// result from the task.
return GetDataAsync();
}
В конце концов, я думаю, вам следует больше соответствовать традиционным асинхронным шаблонам, предоставляемым в .NET (либо шаблон Begin / End, либо основанный на событиях), поскольку они позволят вам легче подключаться к другим конвейерам .