我有一个复杂的基于任务/锁的混乱局面,无法执行“长”数据操作,而我正在尝试将其替换为异步/等待状态。我是异步的新手,所以我担心自己犯了一些大错误。
为简化起见,我的UI中有一些页面依赖于相同的数据。现在,我只需要获取一次此数据。因此,我对其进行了缓存,并且进一步的调用只是从缓存“ 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的项目已添加到字典中。即使我已解决该问题,但根本的问题仍然存在。数据对象将是不同的版本,我正在做两个网络调用,而我应该只有一个,等等。
以前,我通过将整个方法封装在lock语句中来“破解”一个解决方案-从而仅允许对其进行一次调用。我所有的数据加载都是在后台工作人员中完成的,第一个完成了调用,一旦完成,其他人便被解锁以执行快速抓取。
但是我不能在异步方法中使用锁,而且该解决方案还是感觉不好。
异步方法是否可以“等待”其他异步调用完成?
您的问题是在await
将任务添加到字典之前先完成任务。在这种情况下,您需要将任务添加到词典中,以便调用此方法的下一页将获得相同的任务:
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>
类型:
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;
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句