C# 使用任务可避免多次调用昂贵的操作并缓存其结果
我有一个异步方法,可以从数据库中获取一些数据。这个操作相当昂贵,需要很长时间才能完成。因此,我希望缓存该方法的返回值。但是,在异步方法的初始执行有机会返回结果并将其保存到缓存之前,它可能会被多次调用,从而导致多次调用这个昂贵的操作 为了避免这种情况,我目前正在重用一个C# 使用任务可避免多次调用昂贵的操作并缓存其结果,c#,multithreading,task-parallel-library,async-await,task,C#,Multithreading,Task Parallel Library,Async Await,Task,我有一个异步方法,可以从数据库中获取一些数据。这个操作相当昂贵,需要很长时间才能完成。因此,我希望缓存该方法的返回值。但是,在异步方法的初始执行有机会返回结果并将其保存到缓存之前,它可能会被多次调用,从而导致多次调用这个昂贵的操作 为了避免这种情况,我目前正在重用一个任务,如下所示: public class DataAccess { private Task<MyData> _getDataTask; public async Task<MyData>
任务
,如下所示:
public class DataAccess
{
private Task<MyData> _getDataTask;
public async Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return await _getDataTask;
}
}
也许这与结果没有立即枚举有关?使用锁定功能防止多次调用数据库查询部分。Lock将使它成为线程安全的,因此一旦缓存了它,所有其他调用都将使用它,而不是运行到数据库以实现
lock(StaticObject) // Create a static object so there is only one value defined for this routine
{
if(_getDataTask == null)
{
// Get data code here
}
return _getDataTask
}
使用lock函数可以防止多次调用数据库查询部分。Lock将使它成为线程安全的,因此一旦缓存了它,所有其他调用都将使用它,而不是运行到数据库以实现
lock(StaticObject) // Create a static object so there is only one value defined for this routine
{
if(_getDataTask == null)
{
// Get data code here
}
return _getDataTask
}
请将函数重写为:
public Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return _getDataTask;
}
公共任务GetDataAsync()
{
如果(_getDataTask==null)
{
_getDataTask=Task.Run(()=>synchronousDataAccessMethod());
}
返回_getDataTask;
}
这不应该改变使用此函数可以完成的所有事情-您仍然可以等待返回的任务
如果有任何变化,请告诉我。请将函数重写为:
public Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return _getDataTask;
}
公共任务GetDataAsync()
{
如果(_getDataTask==null)
{
_getDataTask=Task.Run(()=>synchronousDataAccessMethod());
}
返回_getDataTask;
}
这不应该改变使用此函数可以完成的所有事情-您仍然可以等待返回的任务
如果这有什么变化,请告诉我。如果您想在调用堆栈中进一步等待它,我想您需要:
public class DataAccess
{
private Task<MyData> _getDataTask;
private readonly object lockObj = new Object();
public async Task<MyData> GetDataAsync()
{
lock(lockObj)
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
}
return await _getDataTask;
}
}
公共类数据访问
{
私有任务_getDataTask;
私有只读对象lockObj=新对象();
公共异步任务GetDataAsync()
{
锁(lockObj)
{
如果(_getDataTask==null)
{
_getDataTask=Task.Run(()=>synchronousDataAccessMethod());
}
}
返回等待getDataTask;
}
}
您的原始代码可能会发生这种情况:
- 线程1看到
,并开始构造任务\u getDataTask==null
- 线程2看到
,并开始构造任务\u getDataTask==null
- 线程1完成构建任务,任务启动,线程1等待该任务
- 线程2完成一个任务的构造,该任务启动,线程2等待该任务
您将得到两个正在运行的任务实例。如果您想在调用堆栈中进一步等待它,我想您需要这样:
public class DataAccess
{
private Task<MyData> _getDataTask;
private readonly object lockObj = new Object();
public async Task<MyData> GetDataAsync()
{
lock(lockObj)
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
}
return await _getDataTask;
}
}
公共类数据访问
{
私有任务_getDataTask;
私有只读对象lockObj=新对象();
公共异步任务GetDataAsync()
{
锁(lockObj)
{
如果(_getDataTask==null)
{
_getDataTask=Task.Run(()=>synchronousDataAccessMethod());
}
}
返回等待getDataTask;
}
}
您的原始代码可能会发生这种情况:
- 线程1看到
,并开始构造任务\u getDataTask==null
- 线程2看到
,并开始构造任务\u getDataTask==null
- 线程1完成构建任务,任务启动,线程1等待该任务
- 线程2完成一个任务的构造,该任务启动,线程2等待该任务
您将得到两个正在运行的任务实例。回答这个问题有点晚了,但是有一个名为的开源库,它将用两行代码为您完成这项任务,并且它最近被更新以处理这种情况下的缓存任务。它也在nuget上提供 例如:
Func<Task<List<MyData>>> cacheableAsyncFunc = () => myDataAccessObject.GetDataAsync();
var cachedData = await cache.GetOrAddAsync("myDataAccessObject.GetData", cacheableAsyncFunc);
return cachedData;
// Or instead just do it all in one line if you prefer
// return await cache.GetOrAddAsync("myDataAccessObject.GetData", myDataAccessObject.GetDataAsync);
}
Func cacheableasync=()=>myDataAccessObject.GetDataAsync();
var cachedData=await cache.GetOrAddAsync(“myDataAccessObject.GetData”,cacheableasync);
返回缓存数据;
//或者,如果你愿意的话,只需在一行中完成
//返回wait cache.GetOrAddAsync(“myDataAccessObject.GetData”,myDataAccessObject.GetDataAsync);
}
默认情况下,它内置了锁定功能,因此每次缓存未命中时,可缓存方法只执行一次,并且它使用lamda,因此您可以一次性执行“获取或添加”。它默认为20分钟滑动到期,但您可以在其上设置任何您喜欢的缓存策略
有关缓存任务的更多信息,请参阅,您可能会发现有用的
(免责声明:我是LazyCache的作者)回答这个问题有点晚,但是有一个名为的开源库,它将用两行代码为您完成这项工作,并且它最近被更新以处理这种情况下的缓存任务。它也在nuget上提供 例如:
Func<Task<List<MyData>>> cacheableAsyncFunc = () => myDataAccessObject.GetDataAsync();
var cachedData = await cache.GetOrAddAsync("myDataAccessObject.GetData", cacheableAsyncFunc);
return cachedData;
// Or instead just do it all in one line if you prefer
// return await cache.GetOrAddAsync("myDataAccessObject.GetData", myDataAccessObject.GetDataAsync);
}
Func cacheableasync=()=>myDataAccessObject.GetDataAsync();
var cachedData=await cache.GetOrAddAsync(“myDataAccessObject.GetData”,cacheableasync);
返回缓存数据;
//或者,如果你愿意的话,只需在一行中完成
//返回wait cache.GetOrAddAsync(“myDataAccessObject.GetData”,myDataAccessObject.GetDataAsync);
}
默认情况下,它内置了锁定功能,因此每次缓存未命中时,可缓存方法只执行一次,并且它使用lamda,因此您可以一次性执行“获取或添加”。它默认为20分钟滑动到期,但您可以在其上设置任何您喜欢的缓存策略
有关缓存任务的更多信息,请参阅,您可能会发现有用的
(免责声明:我是LazyCache的作者)您只需将
作为