2016-12-14 60 views
3

我有一個代碼塊正在讀隊列,處理一個項目(在它自己的線程中),然後重複,直到隊列爲空。無法理解爲什麼我得到一個NullReferenceException

public ActionResult GetOrdersAsync() { 

     int count = 0; 
     SyncDM sync = _common.StartSync(); 

     while (sync != null && sync.SyncId != 0) { 

      int customerId; 
      bool result = int.TryParse(sync.Payload, out customerId); 
      if (result) { 
       Task.Run(() => GetOrders(sync.SyncId, customerId)); 
      } 

      count++; 
      //Process the next Sync 
      sync = _common.StartSync(); 

     } 

     return Json(new JsonModel { 
      Message = "Started " + count + " instances of GetOrders", 
      Success = count > 0 
     }); 

    } 

StartSync()從隊列中刪除一個項目,或者如果隊列爲空則返回null。 GetOrders()處理對象。

問題是有時代碼會在此行上引發NullReferenceException Task.Run(()=> GetOrders(sync.SyncId,customerId));

在調試器中,Sync是null(原因是異常),但customerId有一個值。這告訴我同步在前一行有一個值。這讓我感到困惑,我認爲它與Task.Run和線程有關,但我不明白本地作用域變量是如何自發地改變它的值的。

+0

如果您的同步= _common.StartSync()之前GetOrders和(無效您的同步對象)完成後,它有道理爲什麼會這樣做。您可以將GetOrders和GetOrdersAsync()作爲實際的異步方法,然後等待GetOrders?它會在嘗試處理下一個同步之前等待。 – Dispersia

回答

6

在任務有機會對其進行操作之前,您正在更新sync的引用。請注意,任務不一定立即開始。在某些情況下,進一步降低執行以下後,你的任務可能開始:

sync = _common.StartSync(); 

現在您參考sync是潛在的空,而當你的任務去訪問sync.SyncId,你會得到一個空引用異常。

你的代碼更改爲以下:

if (result) { 
    var syncId = sync.SyncId; 
    Task.Run(() => GetOrders(syncId, customerId)); 
} 

這工作,因爲我們只是想在ID一通。如果你想傳遞對象本身呢?你需要創建一個新的變量,它關閉之外進行修改:

if (result) { 
    var capturedSync = sync; 
    //Assuming GetOrders now takes a `Sync` 
    Task.Run(() => GetOrders(capturedSync, customerId)); 
} 
+1

對於你的最後一個例子,這是一個淺拷貝。更改同步也將更改capturedSync。他需要做一個深層次的複製,在這個複製中我會繼續保持你的第一個例子。 – Dispersia

+2

@Dispersia「改變同步也會改變capturedSync」......你完全確定這一點? – spender

+0

@spender是嗎?這就是淺拷貝所做的。自己嘗試,創建一個對象,分配它(這將通過引用來完成),然後更改新對象,並查看原始更改。 – Dispersia

相關問題