2016-07-28 36 views
2

我有一个复杂的基于任务/锁的混乱,执行'长'数据操作,我试图用异步/等待替换它。我是新来异步等待,所以我担心我犯了一些大错误。async&await - 处理多次调用相同的方法 - 互相锁定/等待?

为了简化,我的用户界面有几页依赖于相同的数据。现在,我只需要一次获取这些数据。所以我缓存它,进一步调用只需从缓存“CachedDataObjects”中获取它,而不是每次都进行长时间调用。

这样的(半伪代码):

private Dictionary<Guid,List<Data>> CachedDataObjects; 

    public async Task<List<Data>> GetData(Guid ID) 
    { 
     List<Data> data = null; 

     //see if we have any cached 
     CachedDataObjects.TryGetValue(ID, out data); 

     if (data == null) 
     { 
      if (ConnectedToServer) 
      { 
       data = new List<Data>(); 
       await Task.Run(() => 
       { 
        try 
        { 
         //long data call 
         data = Service.GetKPI(ID); 
        } 
        catch (Exception e) 
        { 
         //deal with errors (passes an action to do if resolved) 
         PromptForConnection(new Task(async() => { data = await GetData(ID); }), e); 
        } 
       }); 
      } 

      CachedDataObjects.Add(ID, data); 
     } 
     return data; 
    } 

然而,通过异步调用的性质,这种方法获取的由两个页面调用的时候,他们被触发。

因此,有一个例外 - 带有ID的项目已被添加到字典中。即使我修补了这个问题,根本问题仍然存在。数据对象将是不同的版本,我正在做两个网络调用,我应该只有一个等等。

以前,我'通过将整个方法封装在锁定语句中'解决了'一个解决方案 - 从而只允许一个打电话给它。我所有的数据加载都是在后台工作人员完成的,第一次完成了调用,一旦完成,其他人就被解锁以执行快速抓取。

但我不能在异步方法中使用锁,并且解决方案无论如何都感觉不太好。

是否有任何异步方法来“等待”其他异步调用完成?

+0

? –

+0

@PankajGupta对不起,代码中的小错误。 “CachedDataObjects”现在一直用作字典。 – Joe

+0

,抱歉没有得到。 –

回答

3

你的问题是你在将任务添加到字典之前已经完成了任务。在这种情况下,你会希望将任务添加到字典中,以便调用此方法的下一个页面将获得相同的任务:

public Task<List<Data>> GetData(Guid ID) 
{ 
    Task<List<Data>> task = null; 
    CachedDataObjects.TryGetValue(ID, out task); 
    if (task == null) 
    { 
    if (ConnectedToServer) 
    { 
     task = Task.Run(() => 
     { 
     try 
     { 
      //long data call 
      return Service.GetKPI(ID); 
     } 
     catch (Exception e) 
     { 
      //deal with errors 
     } 
     }); 
    } 
    DataObjects.Add(ID, task); 
    } 
    return task; 
} 

这将缓存任务。但是,如果//deal with errors传播异常,那么这也会缓存该异常。

为了避免这种情况,你可以使用更复杂的代码,或者你可以通过我的AsyncLazy<T> type

private readonly ConcurrentDictionary<Guid, AsyncLazy<List<Data>>> CachedDataObjects; 
public Task<List<Data>> GetData(Guid ID) 
{ 
    var lazy = CachedDataObjects.GetOrAdd(ID, id => 
     new AsyncLazy<List<Data>>(() => Task.Run(() => 
     { 
     try 
     { 
      return Service.GetKPI(ID); 
     } 
     catch (Exception e) 
     { 
      //deal with errors 
      throw; 
     } 
     }, AsyncLazyFlags.RetryOnFailure | AsyncLazyFlags.ExecuteOnCallingThread))); 
    return lazy.Task; 
} 
在这种情况下缓存有意义
+0

不错的做法,缓存任务,而不是结果。就我所见,仍然需要锁定才能始终避免缓存测试和分配之间的竞争状态? – grek40

+0

@ grek40:如果您使用的是非并发字典并且正在从多个线程访问此方法,那么是的,则需要锁定。 –

+0

这是一个好主意! – Joe