我有一个需要很长时间才能初始化的对象。因此,我能够在应用程序启动时开始初始化。对类方法的任何后续调用我们都需要有一个延迟机制来等待类完成初始化。
我有几个可能的解决方案,但我对其中任何一个都不完全满意。第一个在 while 循环中使用 Task.Delay,第二个使用 SemaphoreSlim 但涉及一些不必要的阻塞。我觉得这一定是一个相当普遍的要求,有人可以就如何最好地管理它提供一些建议吗?
哦,顺便说一句,这是一个 Metro 应用程序,所以我们的 API 有限
伪代码如下:
public class ExposeSomeInterestingItems
{
private InitialisationState _initialised;
private readonly SemaphoreSlim _waiter =
new SemaphoreSlim(0);
public async Task StartInitialize()
{
if (_initialised == InitialisationState.Initialised)
{
throw new InvalidOperationException(
"Attempted to initialise ActiveTrackDown" +
"loads when it is already initialized");
}
_initialised =
InitialisationState.StartedInitialisation;
new TaskFactory().StartNew(async () =>
{
// This takes some time to load
this._interestingItems =
InterestingItemsLoader.LoadItems();
_waiter.Release();
_initialised = InitialisationState.Initialised;
});
}
public InterestingItem GetItem(string id)
{
DelayUntilLoaded();
DelayUntilLoadedAlternative();
}
private async Task DelayUntilLoaded()
{
if (_initialised == InitialisationState.NotInitialised)
{
throw new InvalidOperationException("Error " +
"occurred attempting to access details on " +
"ActiveTrackDownloads before calling initialise");
}
while (true)
{
if (_initialised == InitialisationState.Initialised)
{
return;
}
await Task.Delay(300);
}
}
private async Task DelayUntilLoadedAlternative()
{
if (_initialised == InitialisationState.NotInitialised)
{
throw new InvalidOperationException(
"Error occurred attempting to access details " +
"on ActiveTrackDownloads before calling initialise");
}
try
{
await _waiter.WaitAsync();
}
finally
{
_waiter.Release();
}
}
}
最佳答案
我认为更好的设计是异步工厂,其中调用代码await
s 对象创建,然后接收一个常规对象实例。
大量偷窃 from Stephen Toub :
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory) :
base(() => Task.Run(valueFactory)) { }
public AsyncLazy(Func<Task<T>> taskFactory) :
base(() => Task.Run(taskFactory)) { }
public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
}
public static class ExposeSomeInterestingItemsFactory
{
public static AsyncLazy<ExposeSomeInterestingItems> Instance
{
get { return _instance; }
}
private static readonly AsyncLazy<ExposeSomeInterestingItems> _instance =
new AsyncLazy<ExposeSomeInterestingItems>(() => new ExposeSomeInterestingItems());
public static void StartInitialization()
{
var unused = Instance.Value;
}
}
public class ExposeSomeInterestingItems
{
public ExposeSomeInterestingItems()
{
// This takes some time to load
this._interestingItems = InterestingItemsLoader.LoadItems();
}
public InterestingItem GetItem(string id)
{
// Regular logic. No "delays".
}
}
...
var exposeSomeInterestingItems = await ExposeSomeInterestingItemsFactory.Instance;
var item = exposeSomeInterestingItems.GetItem("id");
这样,您就可以很好地遵守单一职责原则:
-
AsyncLazy<T>
联合收割机Task<T>
与Lazy<T>
(因此仅在需要时才异步创建实例)。 -
ExposeSomeInterestingItemsFactory
包含构建逻辑。 -
ExposeSomeInterestingItems
只关心暴露有趣的项目,而不是用异步延迟污染它的所有成员。
此外,此解决方案自始至终都是异步的(无阻塞),这很好(特别是对于 Metro 应用程序)。
更新,2012 年 9 月 14 日:我已将此代码整理并对其进行注释 on my blog .
关于c# - 是否有在后台线程上初始化对象的通用模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11261147/