c# – 在后台线程上初始化对象有一个共同的模式吗?

我有一个需要很长时间才能初始化的对象.因此,我有能力在应用程序启动时开始初始化.对类的方法的任何后续调用我们需要有一个等待类完成初始化的延迟机制.

我有几个可能的解决方案,但我对它们中的任何一个都不满意.第一个在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();
        }
    }
}

最佳答案 我认为更好的设计是异步工厂,其中调用代码等待对象创建,然后接收常规对象实例.

窃听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>结合任务< T>与Lazy< T> (因此仅在需要时才异步创建实例).
> ExposeSomeInterestingItemsFactory包含构造逻辑.
> ExposeSomeInterestingItems仅涉及公开有趣的项目,而不是必须使用异步延迟污染其所有成员.

此外,此解决方案始终是异步的(无阻塞),这很好(特别是对于Metro应用程序).

更新,2012-09-14:我已经使用了这段代码并将其清理干净并将其评论为on my blog.

点赞