2012-06-29 48 views
4

我有一個需要很長時間才能初始化的對象。因此,我有能力在應用程序啓動時啓動初始化。任何後續對類上的方法的調用,我們都需要有一個延遲機制,等待類完成初始化。是否有一個在後臺線程上初始化對象的常用模式?

我有一些潛在的解決方案,但我不完全滿意他們中的任何一個。第一個在while循環中使用Task.Delay,第二個使用SemaphoreSlim,但涉及一些不必要的阻塞。我覺得這一定是一個相當普遍的要求,任何人都可以提供一些關於如何最好地管理這個問題的建議?

哦順便說一句,這是一個地鐵應用,所以我們只有有限的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(); 
     } 
    } 
} 
+0

如何不讓實例可用(保持爲空)直到它完成? –

回答

1

我認爲一個更好的設計是一個異步的工廠,那裏調用代碼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-09-14:我已將這段代碼清理乾淨並對其進行了評論on my blog

+0

我在編輯它的確切時間閱讀了這段代碼 - 我得到了一個它已經改變的通知,所以現在去看看這個博客:-)謝謝 –

1

可以使用Task<T>這一點。這會照顧所有的同步爲您和允許你阻止,直到值可用:

private static Task<HeavyObject> heavyObjectInitializer; 

// Call this method during application initialization 
public static void Bootstrap() 
{ 
    heavyObjectInitializer = new Task<HeavyObject>(() => 
    { 
     // creation of heavy object here 
     return new HeavyObject(); 
    }); 

    // Start running the initialization right now on a 
    // background thread. We don't have to wait on this. 
    heavyObjectInitializer.Start(); 
} 

// Call this method whenever you need to use the object. 
public static HeavyObject GetHeavyObject() 
{ 
    // Get the initialized object, or block untill this 
    // instance gets available. 
    return heavyObjectInitializer.Result; 
} 

或者,您也可以查詢,看是否該對象是否可用:

public static bool IsHeavyObjectAvailable 
{ 
    get { return heavyObjectInitializer.IsCompleted; } 
} 
+0

請注意,重對象構造引發的任何異常都將包含在「AggregateException」中。 –

0

將方法調用放入您在完成初始化時處理的隊列中。尚未初始化時,只將方法放入隊列中。

0

您可以轉移到應用程序處於不同狀態的事件驅動體系結構。

最初,應用程序進入開始狀態。在此狀態下,使用後臺任務創建HeavyObject。初始化完成後,會觸發一個事件。 (你不必使用實際的.NET event。你可以使用回調或類似的東西,而Reactive Extensions等框架可以讓你編寫事件序列。)

當所有的初始化事件觸發時,你移動到開始您的應用程序的狀態。對於UI應用程序,這可以修改UI以啓用一些先前禁用的操作。

0

請檢查這個Prototype Pattern。也許它可以幫助你

你只需要創建一次你的對象,並克隆它,當你需要另一個。

相關問題