2016-11-21 36 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

谢谢,你是唯一一个了解问题的人:)只是想给你信用! –