c# - 是否有在后台线程上初始化对象的通用模式?

标签 c# .net multithreading background parallel-processing

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

我有几个可能的解决方案,但我对其中任何一个都不完全满意。第一个在 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/

相关文章:

c# - 如何将用户控件放入文档查看器中?

c# - String.Format C# 每1000个数字之间的间距

.net - 使用缓存从 .NET 调用 M 标记

java - Java中线程安全队列和 "master/worker"程序的模式/原则

c++ - 在使用 libstdc++ 进行调试期间强制在 std::atomic 中使用锁

c# - 如何在 MVVM 模型中管理磁贴用户控件(具有列表框绑定(bind))?

c# - 尝试实现一种可以比较任意两个列表但总是返回 false 的方法

C# - DataFormats.GetDataFormat 如何工作?

.net - Linq-to-Sql中的顺序GUID?

java - 如何强制类的用户在 Android 中的 UI 线程之外使用它?