2014-09-24 65 views
1

我有一個由事件處理程序啓動的函數。在這個函數中,一個新的任務被創建並運行,以便在其他事件處理程序中設置一個稍後需要的全局變量。任務和全局變量c#

這兩者之間還有一些其他功能,它們不會更改這些內部使用的任何變量。但是這就是這樣的樣子。

private void EventWhereINeedTheGlobalVariableLater(object sender, Event e) 
{ 
    //...work... 

    need _variableIneed 


    //....more work... 
} 


private void EventWhereISetGlobalVariable(object sender, Event e) 
{ 
    //....work.... 
    //...make cancellationToken... 
    //groups, isCommonalityGroup are other global variables already set. 

    Task.Factory.StartNew(() => 
    { 
     // Clear variable to get new value 
     _variableIneed = null; 

     // Execute query to get new value 
     _variableIneed = _workingManager.GetValue(groups, isCommonalityGroup, cancellationToken); 

     RefreshView(); 

    }, cancellationToken); 
} 

我運行到比賽條件,我需要_variableIneed變量是第二個事件處理程序中無效,它不能。如果我沒有通過並嘗試創建足夠的事件來崩潰wpf程序,它會正常工作,但即使我這樣做,我也需要它。

我能做些什麼來克服這些競爭條件?

我試過使用.ContinueWith,選項OnlyOnRanToCompletion或任何它。任何其他的事情,我可以嘗試?

**注意我不能通過改變事件的排序/處理/工作方式來做很多事情。這是一個非常漂亮的石頭設計,我只需要解決它,並保持它或多或少。

**更新

我已經使用ParallelExtensionsExtrasOrderedTaskScheduler類也試過,我還是最終得到的變量,我需要一個空引用。

+0

如果在EventWhereISetGlobalVariable處理程序中設置變量之前調用EventWhereINeedTheGlobalVariableLater處理函數,那麼您就無能爲力。 – Sheridan 2014-09-24 14:10:59

+0

你的意思是你在'EventWhereINeedTheGlobalVariableLater'中得到空嗎?可能「任務」沒有完成?你需要同步它。 – 2014-09-24 14:11:52

+0

是@SriramSakthivel我得到一個空值爲我需要的全局變量。 – 2014-09-24 14:13:41

回答

3

當你有一個Task生成一個值不結果設置爲一個全局變量,有一個結果是任務的Result,和存儲任務。當稍後的其他代碼需要這樣的結果時,它可以從任務中獲得它。這將允許Task類處理所有複雜的同步邏輯,防止在任務實際計算出結果之前使用結果等。

當然,對於需要使用結果的事件,它可能是假定的不需要在該任務上阻塞,但在任務完成後,執行需要異步結果的代碼的其餘部分。通過在該任務上使用await可以非常容易地完成此操作。如果你只使用.NET 4.0,那麼你可以明確地使用ContinueWith

+0

那麼,看起來會是什麼樣的結果,成爲任務的「結果」,然後再從任務中訪問它? – 2014-09-24 14:33:45

+0

+1。使用4.5和'async'代碼,或至少連鎖任務。 – 2014-09-24 14:33:57

+0

@Brett'StartNew'的lambda應該返回結果,而不是設置另一個變量。事件處理程序應該「等待」你開始的「任務」。 – Servy 2014-09-24 14:35:08

1

使用Servy的方法 - 異步/任務。此答案嚴格用於娛樂目的,或者如果您不能使用.Net 4.0+或3.5 with Rx


既然你不能改變你要麼需要期待的變量是從時間null時間或防止它被視爲null事件的順序。

一個選項是輪詢此變量,並且只在未設置爲null時才能工作(如果您的EventWhereINeedTheGlobalVariableLater不重複觸發,則可能需要計時器)。

或者,您可以始終保持變量值或防止其他線程看到null值。

防止空從正在成功案例可見,仍然可以爲空,如果「長計算」失敗:

object lockObj = new object(); // at class level 

    private void EventWhereISetGlobalVariable ... 
    { 
    lock(lockObj) 
    { 
     _variableIneed = null; 
     // some long and convoluted computations 
     _variableIneed = someResult; 
    } 
    } 

    private void EventWhereINeedTheGlobalVariableLater(object sender, Event e) 
    { 
    lock(lockObj) 
    { 
     // unsing _variableIneed 
    } 
    } 

防止它僅通過設定值時,我們有一個(或鎖定周圍設置爲null訪問變量或volatile會工作,更喜歡鎖定在其他樣本)。這是緩存需要很長時間計算的某些值的常用模式,如果變量的用戶看到稍微過時的值,則可以。

volatile WhateverYourType _variableIneed; 
    private void EventWhereISetGlobalVariable ... 
    { 
     // some long and convoluted computations 
     _variableIneed = someResult ?? _variableIneed; 
    } 

注:

  • 確保您瞭解如何在代碼中任何鎖定/同步交易 - 所以要非常小心使用該變量在多個地方。考慮用鎖來包裝對變量的訪問。
  • 考慮將值複製到局部變量鎖內,並在需要值的代碼中使用局部變量。否則其他線程可能會將其更改爲新的值/ null。
  • 一次性設置變量考慮Lazy<T>類處理這種初始化或如果使用Cache存儲值更合適。
+0

我有一個問題 - 它會使一個變量鎖定,並行使用嗎?我的意思是如果來自另一個線程,我想獲取或設置這個變量,它將被鎖定 – Sasha 2014-09-24 14:27:35

+0

TPL是專門設計的,以避免要求你做這樣的事情,由於它們添加到代碼的複雜性,出錯的可能性等等。這對於解決這個問題並不是一個有效的方法,因爲他已經在使用TPL了。 – Servy 2014-09-24 14:29:57

+0

@Sasha鎖可以防止多個線程運行用相同的鎖包裝的代碼 - 所以其他線程將一直等待樂aves用'lock'包裹的塊。這將保證當前使用/設置/計算它的值不會被其他線程改變。考慮閱讀[鎖定](http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx)和[關鍵部分](http://en.wikipedia.org/wiki/Critical_section)。 – 2014-09-24 14:31:33