2016-11-21 41 views
0

我們正在開發一個在UWP(前端)和REST-MVC-IIS(後端)中開發的項目。UWP + IIS +異步行爲

我想上可能引發了理論場景:

據我所知,有沒有辦法保證其請求將被處理和IIS服務的順序。

因此,在一個簡單的場景,讓我們姑且這樣的:

UI:

的SelectionChanged(productId參數= 1);

SelectionChanged(productId = 2);

private async void SelectionChanged(int productId) 
{ 
    await GetProductDataAsync(productId); 
} 

IIS:

GetProductDataAsync(productId=1) scheduled on thread pool 

GetProductDataAsync(productId=2) scheduled on thread pool 

GetProductDataAsync(productId=2) finishes first => send response to client 

GetProductDataAsync(productId=1) finishes later => send response to client 

正如你所看到的,對於productId=2要求無論出於何種原因更快地完成然後productId=1的第一個請求。

由於異步工作的方式,兩個通話將建立一個關於這將覆蓋對方,如果他們不按照正確的順序來,因爲它們含有相同的數據的UI兩種續任務。

這可以外推到幾乎任何master-detail場景,在這種情況下,最終可能會選擇主項目並獲取錯誤的細節(因爲響應從IIS返回的順序)。

我想知道的是,是否有一些最佳實踐來處理這種情況......很多解決方案都浮現在腦海中,但我不想在試圖實施之前跳過槍並執行一次實施看看桌上還有什麼其他選項。

+1

兩個UI叫你展示;是他們字面上的兩行代碼一個跟着另一個?如果是這樣,第二行在第一行完成之前不會開始執行,並且整個往返IIS的操作都已完成。 – sellotape

+0

那麼,你錯過了連續兩次調用中使用的異步,這會改變一切,因爲它們不會被順序處理,而是並行處理。這並不是說並行,但這兩個調用的結果稍後將在兩個調用的延續任務中處理,這意味着返回的第一個調用將被首先處理,因此如果第二個請求首先結束(無論出於何種原因)那麼最終會出現上述情況,其中稍後的調用將覆蓋先前調用的結果。 –

+1

如果這2條UI線是連續的,它們將不會並行處理。等待意味着:等到通話結束後再繼續下一行。執行可能會繼續執行調用方法,但在當前方法中不會繼續執行,直到等待完成的任務完成。 – sellotape

回答

0

由於您提供的代碼await GetProductDataAsync(productId=2);將始終在await GetProductDataAsync(productId=1);完成後運行。所以,沒有競爭條件。

如果你的代碼是:

await Task.WhenAll(
    GetProductDataAsync(productId=1), 
    GetProductDataAsync(productId=2)) 

則可能是一個競爭條件。而且,如果這是一個問題,它不是特別針對async-await,但由於您正在進行併發呼叫。

如果換行代碼的另一種方法,並使用ConfigureAwait(),你只有一個UI線程上延續:

Task GetProductDataAsync() 
{ 
    await Task.WhenAll(
     GetProductDataAsync(productId=1).ConfigureAwait(), 
     GetProductDataAsync(productId=2).ConfigureAwait() 
    ).ConfigureAwait(); 
} 
+0

我編輯了我原來的帖子,我已經解釋了更好的情況。這不是關於比賽條件,而是關於女巫在被處理後從國際回來回來的命令。 –

0

我想我明白你在說什麼。由於異步無效事件處理程序,UI中沒有任何內容在第二次之前等待第一次調用。我想象一下數值的下降,當它發生變化時,它會提取相關的數據。

理想情況下,你可能會想在通話過程中要麼鎖定用戶界面或實施cancellationtoken

如果你只是尋找一種方式來米的來電,請繼續閱讀...

我在UWP應用程序中使用單個存儲庫層來處理是否從Web服務或本地緩存副本中獲取數據。此外,如果要計量請求以逐個處理一個請求,請使用SemaphoreSlim。它像鎖一樣工作,但是用於異步操作(簡單明瞭)。

下面是一個例子應該說明它是如何工作...

public class ProductRepository : IProductRepository 
{ 
    //initializing (1,1) will allow only 1 use of the object 
    static SemaphoreSlim semaphoreLock = new SemaphoreSlim(1, 1); 
    public async Task<IProductData> GetProductDataByIdAsync(int productId) 
    { 
     try 
     { 
      //if semaphore is in use, subsequent requests will wait here 
      await semaphoreLock.WaitAsync(); 
      try 
      { 
       using (var client = new HttpClient()) 
       { 
        client.BaseAddress = new Uri("yourbaseurl"); 
        client.DefaultRequestHeaders.Accept.Clear(); 
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
        string url = "yourendpoint"; 
        HttpResponseMessage response = await client.GetAsync(url); 
        if (response.IsSuccessStatusCode) 
        { 
         var json = await response.Content.ReadAsStringAsync(); 
         ProductData prodData = JsonConvert.DeserializeObject<ProductData>(json); 
         return prodData; 
        } 
        else 
        { 
         //handle non-success 
        } 
       } 
      } 
      catch (Exception e) 
      { 
       //handle exception 
      } 
     } 
     finally 
     { 
      //if any requests queued up, the next one will fire here 
      semaphoreLock.Release(); 
     } 
    } 
} 
+0

謝謝,你是唯一一個瞭解問題的人:)只是想給你信用! –